How to get size of an element in physical pixels? - html

Let's say i have a div that i've defined to be (32px, 32px) in size:
html:
<div id="theBox"></div>
css:
div {
width: 32px;
height: 32px;
background-color: gray;
}
(Live jsFiddle view)
How can i get the actual size of the box in pixels?
You'll note that the box doesn't have to be 32px. It can be larger:
or smaller:
or exactly 32 pixels:
The reason for the differences, of course, is because Chrome and Internet Explorer allow me to zoom.
i would like to know the actual size of the element. Why? No reason; just cause. i'm curious, and i'd like to broaden the limits of human knowledge and understanding.
Or because i need to set the internal resolution of a Canvas element to match the actual size of the canvas element - otherwise the rendered canvas contents will get stretched without my permission:
Although, my reasons for wanting to know the size of an element do not necessarily apply just to a Canvas. i'm asking about a generic div element; and the answer will be used towards canvas, img, video, and anything else i desire.

You would need to detect the zoom level.
Then write a simple arithmetic proportion to calculate the 'actual' size, or the size as it appears to the user.
var zoomLevel,
, actualSize = 32
, viewSize;
function getZoomLevel(){ ... your code here...return zoomLevel;}
function getViewSize(actualSize){
viewSize = actualSize*getZoomLevel();
return viewSize;
}
Then ... call getViewSize() when ready ...
Hopefully the math is clear enuff.
Solving for y (or viewSize):
actualSize/1 = y/zoomLevel
However, you will need to be careful about sub-pixel precision, especially among the notoriously bad length/width determining browsers like IE9. But, as long as all you need is something close, this should work.

Related

Misalignment of image and gradient border

For a web application, I need to display small images as a circle and draw a circular gradient-filled border around them using only HTML and CSS. For unknown reasons, some systems reproducibly show misalignment between the image and the border so that they are not concentric. On affected systems, this behavior is visible in both Chrome, Firefox, and Edge, however, the direction of the misalignment is different. Other systems, however, are perfectly fine.
Enlarged screenshot of the misalignment in my web application:
My first thought was, that this might be a subpixel rendering issue but since I am using an even-numbered image size of 24x24px and an even-numbered border width of 2px this seems unlikely. I did some experiments by gradually increasing the image size by 1px and found that the direction and extent of misalignment are inconsistent and sometimes there seems to be an oval distortion. Below you find a reduced code snippet at screenshots from Chrome, Firefox, and Edge. I indicated the direction of misalignment in red. Increasing the border width yielded similar results, but the effect seems most pronounced with 2px.
.rounded-corners-gradient-borders {
box-sizing: border-box;
padding: 2px;
border-radius: 100%;
background: linear-gradient(45deg, #F48ACE 0%, #493A97 100%);
}
<img class="rounded-corners-gradient-borders" src="https://i.picsum.photos/id/368/24/24.jpg?hmac=qTESgqsVn81m_y-i5SDjG0xncWcyf-gYC40Jw9amc-k" />
https://codepen.io/grilly17/pen/VwXNrMO
Annotated screenshot of Codepen output in Firefox:
Annotated screenshot of Codepen output in Chrome:
I am aware that drawing a perfectly concentric "solid colored" border can be achieved a lot easier, but the gradient is a hard requirement in this case.
Since it doesn't seem to affect all systems, I asked friends and colleagues to have a look at different OS types, OS versions, browsers, browser versions, monitors, screen resolutions, and different compute hardware but I was not able to find a common cause for this. The direction and extent of misalignment seemed to be different on every system and browser but it does not change when reloading the page in the same browser again. So it appears to be deterministic.
At this point, my best guess is that it is related to some rounding error in the rendering process, but I would love to get to the bottom of this. Does anybody know why this is happening at all and why it is only affecting some systems? Is there a better solution to achieve this?
Thanks to the hint of "CSS pixels vs screen pixels" I was able to understand the root cause and find a solution to my problem. I should have realized that the screenshot of the icon was 35px high instead of the expected 28px including padding.
Most OS have a display setting for "scaling" up everything on your screen by a certain factor, e.g. 125%. This affects everything on your screen and may cause fractional pixel values, which results in the effect described above. If you have multiple screens, the value might be different on every screen. For web applications, the active screen's scaling value is applied only on page loading/rendering and not when moving the page between screens.
The scaling factor can be accessed via the JavaScript window property window.devicePixelRatio.
Using this I was able to work out two acceptable solutions, which might be useful for others:
Get a "device pixel perfect" representation by undoing the scaling
Get a "no subpixel" representation by accounting for pixel fractions in the unscaled value
The enlarged screenshot below shows from left to right the original misaligned image, the "device pixel perfect" image, and the "no subpixel" image when using a display scaling of 125%.
Here is my code (tested on FF, Chrome, Edge): https://codepen.io/grilly17/pen/QWmegPj
function precompensateScaling(value, scale) {
return value / scale;
}
function precompensatePixelFractions(value, scale) {
return value - value * scale % 1 / scale;
}
// wait until page is fully loaded
window.onload = (event) => {
const original = document.getElementById('original');
const oHeight = parseFloat(window.getComputedStyle(original).getPropertyValue('height'));
const oPadding = parseFloat(window.getComputedStyle(original).getPropertyValue('padding'));
const scale = window.devicePixelRatio;
const unscaled = document.getElementById('unscaled');
//unscaled.style.transform = `scale(${1/scale})`; // alternative
unscaled.style.height = `${precompensateScaling(oHeight, scale)}px`;
unscaled.style.padding = `${precompensateScaling(oPadding, scale)}px`;
const adjusted = document.getElementById('adjusted');
adjusted.style.height = `${precompensatePixelFractions(oHeight, scale)}px`;
adjusted.style.padding = `${precompensatePixelFractions(oPadding, scale)}px`;
};
Thank you all for your support. I <3 the Stack Overflow community!

Is there any difference between user units and pixels?

I've been reading several articles about SVG that make a clear distinction between using and not using units (this last case even has a name of its own), e.g.
<!-- the viewport will be 800px by 600px -->
<svg width="800" height="600">
<!-- SVG content drawn onto the SVG canvas -->
</svg>
In SVG, values can be set with or without a unit identifier. A
unitless value is said to be specified in user space using user units.
If a value is specified in user units, then the value is assumed to be
equivalent to the same number of “px” units. This means that the
viewport in the above example will be rendered as a 800px by 600px
viewport.
You can also specify values using units. The supported length unit
identifiers in SVG are: em, ex, px, pt, pc, cm, mm, in, and
percentages.
source
Is there any actual difference between omiting the unit and setting it to px?
Can I just set e.g. mm everywhere to avoid ambiguity, or I'll eventually be getting different results?
<svg width="800mm" height="600mm">
Disclaimer: what follows is pure guessing (I only learnt the basics of SVG last week) but I'm sharing it because I believe it could help others with my same doubts and I hope it doesn't contain serious errors.
The SVG canvas is basically a mental concept—a infinite plane where you use Cartesian coordinates to place stuff and move around. It isn't too different from stroking shapes in a sheet of graph paper where you've drawn a cross to identify an arbitrary point as coordinate origin, except that notebooks are not infinite. In the same way that tou draw a 3-square radius circle in the sheet and you don't care that those squares represent 12 mm, you draw shapes in your SVG canvas using unitless dimensions because it doesn't really matter what exact physical size they represent. The SVG spec uses the term "user units" to express this idea.
Using actual units only makes sense in two situations:
When our virtual user units need to interact with real world, e.g., the canvas is to be printed in a computer monitor.
When we want an element in our graph to be defined in such a way that it doesn't scale, neither up nor down, e.g. a stroke around a letter that needs to look identical no matter how we resize the logo it belongs to.
It's in this situation, more specifically #1, when the px equivalence comes in handy. When we need to render the graph or make calculations what involve actual units, unitless dimensions are interpreted as pixels. We can think of it as a default because we can render the canvas any size and, in any case, pixels are no longer physical pixels in these days of high-res displays and builtin zoom.
And, for all this, it's probably better to just omit units in your SVG code. Adding them in a general basis only makes code unnecessarily verbose.

Difference in font size on tablets and phones

I really hope, you are able to assist me on this one, as I'm tearing my hair out...
I have a little marquee, based on this code: http://jsfiddle.net/TCJT4/525 that feeds some text.
Here's how it looks on an iPad 6... and please disregard from the preliminary design, but this is how it should look:
Here's how it looks on an iPhone 4S:
The ticker is retrieved from the exact same source, but as you can see, the text appears larger on the iPhone (the iPad image is zoomed, so it appears larger, but in reality, they are both displaying a 320x30 pixels placeholder. The text is temporarily hardcoded to 20px in height and I've tried using other units as well... the banner still looks different on the devices.
I did some debugging of the ticker container/placeholder, as well as the detected banner height and disabled all text-adjusting elements. Here's a result of some of the properties:
iPad 6: Tickerplaceholder DIV-height: 24pixels, bannerheight: 30px, pixelaspect-ratio: 2
iPhone: Tickerplaceholder DIV-height: 32pixels, bannerheight: 30px, pixelaspect-ratio: 2
PC (Chrome): Tickerplaceholder DIV-height: 24pixels, bannerheight: 30px, pixelaspect-ratio: 1;
I find it very strange that two retinadisplay devices display the same banner differently - and that the iPad and the PC displays them correctly.
The ticker can also be found here in its latest form: www.videobanner.dk/ph.html
Pixels are different physical sizes on different devices - so 24px is smaller on one device than on another.
For text, if you use points instead then the size will be the same across devices - they will all make 72pt 1 inch (thereabouts).
Of course this means you have to use text and not bitmaps etc.
Mobile devices may also have a zoom level set for readability (by the user) which will also affect the size - eg you specify 24pt or px and the browser makes it 36pt or px - the calculated size in the inspector will be different to the styled size - to get around this you need to set a value somewhere, then see what it actually is when rendered and apply a ratio to get what you want (via javascript). I've used code like this in the past to ensure text fitted in a box of a given pixel size;
var fontScale = 1 ;
var mySpecifiedFontSize = 24 ;
var myTextElement = document.getElementById("MY_TEXT_ELEMENT_ID") ;
function fontScalingCorrection(){
var style = window.getComputedStyle(myTextElement);
var fontSize = parseInt(style["font-size"]);
if(!isNaN(fontSize)){
if(fontSize !== mySpecifiedFontSize){
fontScale = (mySpecifiedFontSize / fontSize) * fontScale ; //allows for multiple calls
myTextElement.style.fontSize = (fontScale * mySpecifiedFontSize) + "px" ; //or units used
}
}
}
//after the element has been drawn once ( or use another element as a size marker )
fontScalingCorrection();
The cause of the problem is related to a quirk or error in iOS Safari, which returned an incorrect and unpredictable height when dealing with unordered lists, containing text of various lengths. This became apparent when I compared different text lenghts on different platforms. No fix has been found, but I was able to circumvent the problem by splitting one string into several shorter strings, such as
<li>This is a text that </li><li>doesn't go well with iOS</li>
In my project, this solution also works... perhaps not that pretty, though.

How to tame SVGs as background-images

I have the following goal: I wanted to place a heart within a container -  scaled and positioned.
First I wanted to use an icon font but I've discarded the idea. Second option to load the heart as an image I've discarded too - I have to use the heart a few times on my recent project and I wanted to save http requests. Therefore I wanted to go with the SVG as a background-image option. But the problem is, somehow I am unable to tame that beast. I've built a sample pen to illustrate the issues and parts I don't understand.
The un-base64-encoded optimized SVG looks like that:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 960"><polygon points="756,168.4 593.5,168.4 480,258.3 366.5,168.4 204,168.4 30,349 480,791.6 930,349"/></svg>
The sample code you can find from my codepen.
Basically I have three related questions (normally I prefer to post separate issues but those three questions are basically way too connected therefore I hope it's ok):
The sizing: .heart1 has a width and height of 100% and everything displays fine. If you use suiting px values all is fine too but if you try to enter ems the heart isn't shown anymore. Why?
The green box: .heart1 has a width of 100% but if you drag the browser window bigger the heart only grows to some point and then only the green box keeps on growing. I thought SVGs are more or less able to scale to "infinity"?
The yellow box: My basic goal was to make the heart a bit smaller than the width of the yellow box, center it horizontally within and give the heart some top margin. Width and height of .heart2 are set to 75%. But somehow I am unable to position the heart within the box neither with top, left and/or right properties nor in background:url with "no-repeat center 2em" e.g. . It just doesn't react.
I use a block of code shown below to fit svg in a DIV. It works best in a DIV with the same width/height. As you can see below it uses getBBox() to change its viewBox, plus changes the svg width/height values.
It works cross browser: IE10+/CH31/FF23
var bb=mySVG.getBBox()
var bbw=bb.width
var bbh=bb.height
//--use greater of bbw vs bbh--
if(bbw>=bbh)
var factor=bbw/divWH
else
var factor=bbh/divWH
var vbWH=divWH*factor
var vbX=(bbw-vbWH)/2
var vbY=(bbh-vbWH)/2
//---IE/CH---
if(!isFF)
{
var ViewBox=mySVG.viewBox.baseVal
ViewBox.x=vbX
ViewBox.y=vbY
ViewBox.width=vbWH
ViewBox.height=vbWH
}
else
mySVG.setAttribute("viewBox",vbX+" "+vbY+" "+vbW+" "+vbH)
//--requred for FF/CH---
if(!isIE)
{
mySVG.setAttribute("width","100%")
mySVG.setAttribute("height","100%")
}
else
{
mySVG.removeAttribute("width")
mySVG.removeAttribute("height")
}
The svg is centered both left/right and top/bottom within the DIV, plus maintains its aspect ratio. This should help get you started.

HTML5 canvas style height vs attribute height

I am currently learning how to use HTML5 new elements and I stumbled upon a frustrating issue with all graphic functions such as fillRect() and drawImage().
I was setting the widgth and height with
style="width: 75px;height: 10px;"
instead of
width="200" height="100"
The result being that the graphics would not be the proper scale ever.
My question is, why does it make a difference? There is something I obviously do not understand about it all.
Thank you
I know that at least in JSF (specifically primefaces) the difference is that if you put height in the style- it will not be used to properly calculate and render the component (the more complex ones) sometimes. If you put it as attribute then it will work.
If HTML5 takes the similar approach it would mean that attribute height and width are the actual height and width of the component and the style is just the way to display it. Sometimes however, both approaches are going to end with the same result.
Also in primefaces when you specify height and width as attribute- you can not use percentages. This could be the key- an additional measure of enforcing specific width and heights rathen than percentages.
It was an extremely frustrating issue trying to figure out why canvas
doesn't render its shapes correctly despite doing everything right.
The fix was to set "canvas.height" and "canvas.width" correctly. Just setting "canvas.style.height" and "canvas.style.width" doesn't
rectify internal rendering of the canvas.
Almost every time, your
canvas.height = canvas.style.height
AND
canvas.width = canvas.style.width
I read somewhere that the HTML engine refers "canvas.width" and "canvas.height" while doing the calculations for painting shapes and text inside the canvas.
Whereas, "canvas.style.height" and "canvas.style.width" only determine how the browser displays that canvas alongside other HTML DOM elements. Thanks to bjedrzejewski and Munsta0 for asking this question.