React Native Dimensions Inconsistent Units - javascript

I have a view width that has been set to 80 (using Native Base):
<Box
maxW="80"
w="80"
rounded="lg"
overflow="hidden"
borderColor="coolGray.200"
borderWidth="1"
>
There should be enough for 3 of these components to fit across a screen. When I use const { width } = useWindowDimensions(), the value for my iPad mini comes back as 1133 for the width.
When I do an inspection of the component, it comes back as being 328 in width.
How would I go about determining how many items can fit in a row when the dimensions don't seem to line up? E.g, Converting 80 to 328, or reducing 1133 to be in the same units as the view?
Thanks for your help!

Related

Where are the magic numbers come from in `fabric.js`?

https://github.com/fabricjs/fabric.js/blob/master/src/shapes/text.class.js#L210
_fontSizeFraction: 0.222,
_fontSizeMult: 1.13,
Is there any clue how they come from ?
I found they are very useful to measure the actual text size.
_fontSizeFraction is approximately equal to 2/9, but my impression is that this value would be used to decrease the default size of the font.
Text Line proportion to font Size (in pixels)
_fontSizeMult increases values by 13%. It takes the font height, increases it by 13% to give you the total line height including line-spacing.
These variables look like defaults and were likely chosen by the developer based on personal preference.
They work for finding the text size because the text size is likely defined by these variables.
check this "issue" on fabricjs github where the fabric dev explain this
https://github.com/fabricjs/fabric.js/issues/2059#issuecomment-85897275

How to set font-size to "larger" or 140% of system default

I want to provide to my users a possibility to display certain <Text/> elements with a larger font-size, than a system default. The documentation provides a number only for that style.
I want to display texts either with web-css "large" or "larger" values or maybe with 140% of the original font size.
I found couple of questions on SO and 3rd party libs, like responsive-fontsize, but they all seem not relavant to me.
What's the easiest way to do what I want?
You define styles for each component, such as <Text /> you described. Here is an article
You can set your fontSize as kind of formula, example: style={{fontSize: 10*1.4}} or style={{fontSize: x*1.4}} where x is your global variable.
P.S. Normally, you can't just make font bigger in %
You can use em for a relative size.
With em, you can say how many times bigger or smaller you want your text to be. For example:
0.5em will be half of your initial size, 1em will be the same size, 2em will be twice the size.
<Text style={{fontSize: `${relativeSize}em`}}>
I am {relativeSize} times bigger than I was supposed to be
</Text>
Here is a working example:
https://snack.expo.io/#andrpr/bigger-text-example
If you want to use percentages, you can convert them to how much bigger you want the text to be:
const percentage = 140;
const relativeSize = percentage/100;
https://snack.expo.io/#andrpr/percentage-bigger-text-example
I think what you're looking for is PixelRatio which let you more control on your fonts sizes. Or else it could help someone who reached your post.
PixelRatio gives you access to the device's pixel density and
font scale.
So after you got the informations you want you can play with it.
You can find exemples on this answer
For exemple :
var React = require('react-native');
var {StyleSheet, PixelRatio} = React;
var FONT_BACK_LABEL = 18;
if (PixelRatio.get() <= 2) {
FONT_BACK_LABEL = 14;
}
var styles = StyleSheet.create({
label: {
fontSize: FONT_BACK_LABEL
}
});
I would create a custom Text component that translates the defined textSize style to the adjusted scale. The scale you can get from a Context-Provider, Redux or Recoil, etc.
something like this (untested):
export function MyText(props) {
const { fontScale } = useContext(TextContext);
return (
<Text {...props} style={[...style, { fontSize: style.fontSize ? style.fontSize * fontScale : undefined }] />
);
}
After some experimenting I figured out that the default font size - if not set and for Android at least - corresponds to 14.
From that base one can increment the size to 18 to get roughly 140% of the original size.
Good thing is the font is scaled depending on system-wide font size.

Responsive Heatmaps

The Problem
I've encountered a slight problem where I've written some code where I've automated a script to track the users interactions with a UI, including mousemove & click events. If I didn't have to worry about making it responsive, then I could probably call it a day & ship the work that I've done already, however my brain is seeking some wizardry knowledge, I'm struggling to make it super responsive. Here's just a quick example of the kinda thing that I'm working on, it's nothing genius, if anything I it's mostly heatmap.js that's doing the heavy lifting. Currently I'm just seeing if I can do this as a proof of concept more than anything else...
The Code
So currently, I'm tracking the event.pageX & event.pageY values to store exactly where an event took place, I'm also storing the window.innerWidth & window.innerHeight values to try & work out some function that would allow me to offset the positions based on the size(s) of other devices.
E.g. If you look at the sample image above, that's perfect for a static page, but if I were to say make the page a little more narrow, you can see here that it's doesn't line up with the image above:
Anyway, without blabbering on too much, here's some sample code:
// A lot of other code...
var setupHeatMaps = function (pages, heatMaps) {
pages.forEach(function (page) {
page.addEventListener("click", function (event) {
heatMaps.push({ x: event.pageX, y: event.pageY, value: 10000 });
onStateChange();
});
// Don't collect ALL mouse movements, that'd be crazy, so collect
// every 1/10 mouse movements.
var counter = 0;
page.addEventListener("mousemove", function (event) {
if (counter === 10) {
heatMaps.push({ x: event.pageX, y: event.pageY, value: 20 });
onStateChange();
counter = 0;
} else {
counter ++;
}
});
});
};
// A lot of other code...
// Curried function so that it can be passed around without exposing the state...
var renderHeatMaps = function (heatMaps) {
return function () {
var max = heatMaps.length;
var points = heatMaps;
var parent = getParentElement();
var styleObj = window.getComputedStyle(parent);
var div = document.createElement("div");
var body = document.querySelector("body");
var background = document.createElement("div");
// This element needs to sit in front of the
// background element, hence the higher z-index value.
div.style.position = "absolute";
div.style.zIndex = 9;
div.style.left = "0px";
div.style.top = "-80px";
div.style.width = "100vw";
// Even though this element will sit behind the element
// that's created above, we will still want this element to
// sit in front of 99% of the content that's on the page.
background.style.position = "fixed";
background.style.top = "0px";
background.style.left = "0px";
background.style.height = "100vh";
background.style.width = "100vw";
background.style.zIndex = 5;
background.style.backgroundColor = "rgba(255, 255, 255, 0.35)";
background.setAttribute("id", "quote-customer-heat-map-background");
var heightInPx = styleObj.getPropertyValue("height");
var rawHeight = parseInt(heightInPx.replace("px", ""));
var newHeight = parseInt((rawHeight + 80));
div.style.height = newHeight + "px";
div.setAttribute("id", "quote-customer-heat-map-foreground");
body.style.paddingBottom = "0px";
body.appendChild(background);
body.appendChild(div);
var heatMap = h337.create({
container: div,
radius: 45
});
heatMap.setData({ max: max, data: points });
};
};
// A lot of other code...
As the pages elements that you can see being used in setupHeatMaps changes in width, viewing this data gets offset quite badly. I'll be honest, I've spent a lot of time yesterday thinking about this issue & I've still not thought of anything that seems reasonable.
Alternatively
I have wondered if I should somehow just store the page as an image, with the heatmap overplayed, that way I wouldn't really have to worry about the heatmap being responsive. But then I need to figure out some other things... E.g. Versioning this data, so in the event that a user views a page on their phone, it'll stored that data separately to data that was collected from a previous session where they were on a laptop or on a desktop device.
Conclusion
I'll be honest, I'm not entirely sure what the best course of action is, have any of you guys encountered anything like this before? Have you guys thought of something genius that solves an issue like this?
P.S. I would share a lot more code, however there's an immense amount of code that goes into this overall solution, so I kinda can't share all of it, I'm pretty sure the guys at Stackoverflow would hate me for that! 😅 - You can also tell that I'm doing this as a POC because normally I'd just offload the rendering of the background element & the div element to the underlying framework rather than do it programatically like this, keep that in mind, this is just a POC.
Perhaps you could change the setup and record what element an event is on and save the location data relative to that element, not the whole page.
You record all events on the page still but you save the data relative to the elements instead of the whole page, which prevents you from mashing the data together in your heatmap afterwards when you view
it over your website at various widths.
Say you you have a setup like this in pseudocode:
#1000px width
[html width 1000px, height 500px
[div width 800px, height 400px centered
[button1 width 100px, height 30px centered]
]
]
#500px width
[html width 500px, height 1000px
[div width 400px, height 800px centered
[button1 width 80px, height 30px centered]
]
]
Your users move over some element at all times. Just capture that data like this, example data from 2 users at the two different screen sizes moving the cursor towards the button in the center:
user interaction{
over element: html {dimensions: width 1000px, height 500px}
user position: {x 900, y 20}
}
user interaction{
over element: html {dimensions: width 1000px, height 500px}
user position: {x 850, y 60}
}
user interaction{
over element: div {width 800px, height 400px}
user position: {x 700, y 60}
}
user interaction{
over element: button1 {width 100px, height 30px}
user position: {x 90, y 10}
}
user interaction{
over element: html {dimensions: width 500, height 1000px}
user position: {x 450, y 100}
}
user interaction{
over element: div {width 400px, height 800px}
user position: {x 380, y 40}
}
user interaction{
over element: button1 {width 80px, height 30px}
user position: {x 60, y 10}
}
Then when you view your website draw the heat over all elements and calculate the relative position of the captured data.
So when you would view your site at #500px width the heat over your button1 would be as following:
[button 1 width 80px, height 30px
heat 1, x 72px y 10
heat 2, x 60px y 10
]
And you do the same for all other elements. I don't know how useful the data is like this, but it was for the sake of weird wizardry right?
Personally I'd just exclude data based on the screen width your viewing your heatmap at. That way you can use this setup and get useful heatmap data. So you'd exclude data based on if it was captured at a specific responsive width. So you could at the very least mash all user data together at high widths cause you'd know your interesting web elements would probably be centered still and the same size.
Its common to cut up your responsive design in 2 or 3 sizes, monitor, tablet and phone. You'd be surprised how similar you can keep your design layout across those 3. The more similar the more useful it will be to mix the data from different width that will fall into the specific media query range.
As long as you use the same technique for getting the width and height for saving your data as painting it later it will be fine. Even if you'd ignore margins and borders for instance your event would still capture on the element when its in the margin and that way you could get data like this: user position: {x -10, y -1} and still use that to paint your heat just fine on the element.
You could also give the option to mix and filter the user data across different size to the user, just call it experimental data mixing or something. You could potentially still get some very useful visual information on the most clicked elements for instance if you mix all user data regardless of screen size.
Another thing you could do is soft-mix the results. If your user data differs too much in width and height from the current element's dimensions, say more then 30%, you could exclude it. You could make this dynamic by letting the user set the sensitivity (feathering) of that percentage. That way you can still view all user heat on all smaller elements while it ignores the not very relevant user events on your larger more changeable elements.
My understanding of the issue is that the project works well, in terms of correctly performing the events, storing the information and displaying it, yet, there is a UX problem, because the system is offsetting (in time, I assume). I would use an idea which resembles very much compression. Imagine your monitor as a grid of pixels. It's perfectly clear that there are some precision problems, because even if we use the maximum precision our computer and its components allows us to use, if we take two adjacent pixels and want to click in between the two, we quickly realize that the precision our system offers is quite limited, which is axiomatically true for digital devices, which are unable to respect the Hausdorf separation.
Now that we have explored the limitations with our little thought experiment, we can acknowledge that such limitations exist. Hence, in principle, lowering the number of pixels, but not the number of events would not introduce more problems, it would decrease spatial precision though for a certain amount (the amount that you deem to be still acceptable, off course). So, if you have x * y resolution applied for the map, then you can think about your resolution as "every 5 pixels in with and height counts". That would make a mapping between actual pixels and imaginary pixels that would form the basis of our compression, decreasing the clickable places 25x.
This would allow you to think about the monitor as a set of regions, the number of regions being significantly smaller than the actual number of pixels (25x in our example). That would allow you to avoid storing each coordination/click separately and instead, you could always store the (x, y, n), that is, the center of the region where the event occurred and the number that event was repeated.
This way if the user clicks on a pixel (x', y'), which is located in a region whose center is (x, y), then you would only have new data if a click did not occur in that region yet. Otherwise, if a click occurred there (not necessarily at the exact same pixel, but in the region), then you would just increase n. As a result, instead of a very large set of raw pixel-event data you would have a much smaller set of pixel-event-number data.
The drawback of this approach, off course is that it somewhat reduces geometrical precision, but it should contribute to the optimization of data processing.
I confess that I'm not very experienced with Heatmap.js, so I'm unsure whether it has API support for what I am suggesting. If it has, then trying to use it would make sense. If it is not optimized properly, or does not support such a feature, then I would implement the heatmap feature using a canvas or an svg, depending on the actual needs.
While this solution may not be the most elegant solution ever, it does at the very least work, if anyone has any ideas or anything that's just plain ol' better, by all means chip in!
My Solution Explained
So I was thinking, I could render this via an iframe, sure it's not the most beautiful solution ever, but at least that means that there's no complicated positioning logic involved, it's relatively lightweight in terms of computational complexity compared to some other solutions I've looked at & that it works for all sorts of screen sizes, it essentially removes the need for this to be responsive... Kinda...
Sure that means that you'll have scroll bars left, right & centre, but it's simple, straight to the point & it means that I can produce an MVP in very little time, and realistically, I'd expect your average junior developer may have an easier time understanding what's going on? What do you guys think? 🙂
I'm trying to think of it from a user perspective too, realistically in this kinda application, I would much prefer true, raw accuracy over something looking so nice it makes me want to become a designer. I've even done some homework into this subject, as I've essentially been trying to build some session-replay software, it has been a bloody interesting project/feature I must say! 😀
With some styling, I've been able to accomplish something like this... Obviously this isn't a direct snippet of the website/application, but this is some marketing material that my boss has created, but the content within the 'laptop', that is an actual screenshot/snip of the web application! 😅 - So all in all, I think it looks okay? - You can see where the overflow happens & the right hand side of the screen is a bit cut off, but because everything else within the UI sorta behaves like they have a fixed position there, I personally think it seems to work pretty darn well.
Edit
I'd just like to thank everyone that spent their time, whether that was adding comments or providing answers, fantastic to see the dev community be so helpful! Thank you all guys! 😀
Blog Post
So I've written a little more on this subject matter here:
LinkedIn Post
Blog Post

Responsive D3 Charts in React: ResizeObserver or the viewBox attribute?

I recently came across this video from a tutorial series on D3 + React. In this video, to resize the graph, a custom hook is created that uses ResizeObserver:
const useResizeObserver = ref => {
const [dimensions, setDimensions] = useState(null);
useEffect(() => {
const observeTarget = ref.current;
const resizeObserver = new ResizeObserver(entries => {
entries.forEach(entry => {
setDimensions(entry.contentRect);
});
});
resizeObserver.observe(observeTarget);
return () => {
resizeObserver.unobserve(observeTarget);
};
}, [ref]);
return dimensions;
};
I've always seen previously / used before the viewBox attribute to make SVG's responsive, as this article (and many others) discusses. My question is: is there a reason (performance, simplicity) to use this useResizeObserver custom hook over simply using viewBox? With viewBox, I don't need to create a custom hook, or have a wrapper div around my SVGs (as he explains in the video), or have to use refs... viewBox seems simpler. However, apparently ResizeObserver is a new API to get the dimensions of elements, and perhaps there are advantages to using it over viewBox that are not obvious.
Issues with viewBox
Although viewBox is the quickest solution, it has several downsides:
viewBox tries to satisfy the aspect ratio. If your chart doesn't have an aspect ratio of 1:1, this happens:
The chart didn't fit itself to the container. Instead viewBox kept the aspect ratio and added padding. We can stretch with preserveAspectRatio=none:
Chart (and text) scaling. Not preserving aspect ratio introduces another problem. If you use height and width, you can define your text to be a certain size in CSS - but not with viewBox. Take the case of a 0 0 600 300 viewBoxed chart fitting a 1200*600 page. In this case, everything gets scaled, including text, as you can see above: you can no longer set the font size. This won't happen just to text: other elements that don't stretch perfectly, like circles, will have the same issue.
At first glance, viewBox seems much simpler. But because of the footguns highlighted above, viewBox becomes very complex to use in practice.
Manual scaling
The solution is to size the chart manually using height and width, avoiding all the above issues. Say we have a chart component that renders itself based on height and width. The process becomes:
set the width and height on the first render, fit to the container. The container needs its own width and height set.
Use a ResizeObserver on the container, which will notify us when its size changes.
When the chart is resized, get the new height and width, and use them to render the chart.
When the height and width are set manually, we size only what needs to be scaled. For example, with the above bar chart:
We could set the bars' size relative to the height and width of the chart.
If we have text, we could position it relative to the height and width, but make the text size fixed, which would be impossible with viewBox, since it scales everything:
Conclusion
Use viewBox as a quick solution if you have a chart with elements that don't get affected by stretching: for example, with rectangles, or if you don't care about the above issues.
Use manual scaling if you have elements that cannot be stretched, like text or circles, and need text to stay the size you tell it to.
If you don't care about the aspect ratio issue, and can work around your text being scaled up or down, then viewBox can be the right choice, as long as you're aware of its pitfalls.
However, in practice, manual scaling ends up being the better choice in the vast majority of cases, because there are very little cases where you have only stretchable elements in a chart.
Sources
Images taken from Responsive SVG charts — viewBox may not be the answer.

iPhone 4 screen.width reports 320 - I thought it was more?

In JavaScript, screen.width reports 320 on my previous generation iPod touch. On an iPhone 4 it also reports 320. I thought the new iPhone 4 "retina" display was more? Why would it report 320?
The screen is treated as an "Extra high resolution" 320 pixel for most purposes. Even though literally it has more pixels.
(Not web-page, but informatively: Old pre-retina apps run, the same, but sneakily load the specially named double-resolution images, if they're found. (image.png vs image#2x.png for example.) And drawing commands work as if it was 320 pixels.)
This is just an assumption, you may get this because the physical pixels is "hidden" from the user which. So 320, does not mean 320pixels but 320 points, an relative size. 1 point maps to 1px on the "old" iPhone, but 1 point maps to 2pixels on the new Retina screen. Again, just an assumption....
Best approach to getting a value that makes sense seems to be doing:
var dpr = Math.ceil(window.devicePixelRatio);
var actualSize = cc.size(screen.height*dpr, screen.width*dpr);

Categories

Resources