Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rounding CSS Subpixels on transforms

So, this issue has come up before, like here: Translate + Canvas = Blurry Text and here: Is it possible to "snap to pixel" after a CSS translate?

There doesn't seem to be any conclusions on either of those links—or any other articles I've read. Some responders didn't think it was important enough to care, so here is why in my situation it is: Screenshot in Chrome 41.0.2272.104Screenshot in Chrome 41.0.2272.104

Screenshot in Safari 8.0.4 (10600.4.10.7)Screenshot in Safari 8.0.4 (10600.4.10.7)

See the loss in detail in Safari? (look at the structure in the space-shuttle image, or the detail in the rocks in the 3rd image)

The CSS for these guys is

width: 100%;
height: auto;
position: relative;
top: 50%;
-webkit-transform: translateY(-50%);

So, in some of these situations—the translateY will end up in a half pixel. The first image on the left ends up with a transform matrix like so:

-webkit-transform: matrix(1, 0, 0, 1, 0, -56.5);

At the current time, it seems chrome is rendering this nicely (i've seen some folks say different browsers create the issue in different versions), but currently Safari is having the issue. So, my assumption to fix this issue is to make sure that there are only whole pixels, which I've already done by doing the math and applying the transform in javascript, but this costs more in performance time when running on a lot of images.

I've tried a few CSS-only hacks like using scale3d with no success. If anyone has any JS-free solutions, I would much appreciate the shared knowledge.

like image 834
RooWM Avatar asked Mar 25 '15 21:03

RooWM


1 Answers

In some browsers, you can take advantage of floating point rounding errors from calc to round your number to the nearest increment desired:

calc((2.55px * 5e-324) / 5e-324)

should cause 2.55px to get rounded up to 3px. The supporting browsers are Chrome (including derivatives like Brave, Electron, Edge, and Opera) while the unsupporting browsers are IE, Firefox, and Safari (including derivatives such as Midori). Thankfully, the unsupporting browsers, IE, Firefox, and Safari, just disregard the calc as being an invalid property value because the numbers used are outside the acceptable range. So, to utilize this, just use the example below to generate CSS to suit your needs. Yes, I know that this generator doesn't always combine like-terms, and yes, there is a reason: combining those like-terms would create a number unable to be stored.

var unit = document.getElementById('unit'),
  precision = document.getElementById('precision'),
  property = document.getElementById('prop'),
  output = document.getElementById('output');

function formatProp(x) {
  return (property.value.indexOf('%s') ?
      property.value.replace(/%s/g, x) :
      proprty.value + x).replace(/\\n/g, '\n')
    .replace(/\\t/g, '\t').replace(/\\r/g, '\r');
}
(unit.oninput = precision.oninput = property.oninput = function() {
  var Val = parseFloat(precision.value),
    V1 = "5e-324",
    V2 = "5e-324";
  if (Val < 1)
    V1 = V2 = '' + 5e-324 / Val;
  else if (Val > 1)
    V2 += ' * ' + Val, V1 += ' / ' + Val;

  output.textContent = formatProp(unit.value) + ';       /* for IE and FF*/\n' + formatProp('calc((' + unit.value + ' * ' + V1 + ') / ' + V2 + ')') + ';';
})();
CSS Unit: <input type="text" id="unit" value="-50%" style="width:14em" /><br /> Property : <input type="text" id="prop" value="-webkit-transform: translateY(%s);\ntransform: translateY(%s)" style="width:40em" /><br /> Round to: <input type="number" id="precision"
  value="1" style="width:14em" /> pixels (can be decimal)<b5 />
<pre id="output" style="background:#eee"> </pre>

Please note that as per the lingual definition of real-time responsive, yes, you can enter in your own values into the demo above, and yes, the corresponding CSS will be generated realtime.

My Testing Page I created: purposeful-rounding-errors browser support test

Please note that while the chart above features currently supporting browsers, it is very much subject to change because utilizing rounding errors is sort of non-standard: the W3C spec only implies them in the definition of a floating point number, but does not ever explicitly state that browsers need to implement sub-normal floating point notation, or rounding errors.

like image 134
Jack G Avatar answered Nov 16 '22 04:11

Jack G