I'm implementing a Pinch to Zoom gesture in JS/CSS. In order to do so, I'm putting a fairly large image source inside a small sized IMG tag and then apply a CSS scale transform to make it bigger.
Things are working fine however on Mobile Safari the image gets blurry once I start scaling up. It should not be the case as the src for the image is big enough to support this size, but somehow Mobile Safari is displayed it as blurry.
I have debugged the issue quite a bit and found out that the culprit is an unrelated div in my page that is setting translate3d(0,0,0). To my understanding this is a Hack to trigger GPU hardware acceleration. What I believe it is happening is that because of translate3d(0,0,0), hardware acceleration is being applied when rendering the <img/>, and since the <img> is small in size, it is drawn small and the scale is applied afterwards.
To better understand the problem, I have it this code, available in in codepen here
HTML
<div>
<div id="translate3d-div"/>
</div>
<img id="image" src="https://www.trackalytics.com/assets/thumbnails/lipsum.com.jpg"/>
CSS
#translate3d-div {
/* If I remove this, the image is not blurry */
transform: translate3d(0,0,0)
}
#image {
width: 95px;
height:76px;
transform: scale(20);
}
This displays the image blurry on Mobile Safari, but it works fine on Chrome in Android.
Is there a way to fix this other than removing the unrelated translate3d? I don't think I can do that since it might be used by some other feature in the webpage.
This is because how WebKit renders transforms when hardware acceleration is enabled (like in iPhone). It increases performance dramatically but affects antialiasing when you apply a 3DTransform when no real transformation happens.
A hackfix is to set a background for the element.
#translate3d-div {
transform: translate3d(0,0,0);
background: transparent;
}
Source: https://dropshado.ws/post/6142339613/resolving-anti-aliasing-on-webkit
I couldn't find a working hack for this, but the workaround I used was to simply create a bigger HTMLImageElement and scale it down initially. Then scaled up it will not look blurry. This made my code slightly more complicated but it is working fine.
Related
Here's a reproduction of my issue (codepen because apparently stackoverflow's iframe affects the rendering somehow):
https://codepen.io/Ironimus/pen/GRjxpXZ
When using transform: translateZ and perspective inner elements' edges and text become blurry. It works very differently in different browsers, too. In case it also depends on screen or OS, here are a few screenshots with descriptions:
In Google Chrome transform: scale(2) is fine. Text and inner elements become blurry depending on the width of the element with perspective, and it follows very strange logic, when I set the width to 847px or higher it breaks, lower looks ok:
In Mozilla Firefox text becomes blurry if perspective/translateZ combination is used, inner elements are fine, transform: scale(2) is ok as well. width doesn't seem to affect anything:
In Safari everything is hella blurry whatever I do:
Why is it blurry at all and how do I make it right? Using transform: scale(2) for everything isn't a viable option. Are there maybe some rules I should follow to help browsers render 3D transforms with text right?
Supporting Safari isn't a priority but would still be nice
Have you tried adding a custom -webkit to the browser?
I am probably late with this answer but I recently encountered the same behavior when implementing a parallax design for a website.
This is a problem related to text being rendered as bitmaps in Safari, which are not re-rendered on transform: scale(x); but instead blown up in size.
It is possible to manually trigger a re-render of the text in some cases, however this is very finicky and there's no guaranteed way to make it work.
A more comprehensive answer to this was provided by Jack in this thread:
Transform scale() Safari bug when used on elements with border
I try to scale down pictures (representing rendered text).
Something I noticed is that different browsers will give very different results in the way they are handling the scaling and the anti-aliasing after that (it seems that they almost all apply that by default).
Here is a jsfiddle (I can only rely on the results on my Mac and to see the problem, it has to be tested on different browsers too):
https://jsfiddle.net/ottmobk9/7/
<div class="test"></div>
<script>
/* I added an animation and a loop in order for you to see how Safari and */
/* Opera handle the scaling (after the scaling, it's sharp for one second */
/* then it becomes blurry) but no animation or loop is needed. If the */
/* picture is downscaled, it loses its sharpness. */
function testResize()
{
setTimeout(function()
{
x=$('.test>img').height();
if(x>55)
{
$('.test>img').css('height',(x-1)+'px');
testResize();
}
},5);
}
setInterval(function()
{
$(".test>img").remove();
$(".test").append("\
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/T.png'>\
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/E.png'>\
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/S.png'>\
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/T.png'>\
");
setTimeout(testResize,1000)
}, 3000);
</script>
Firefox (on Mac): The original size is a bit blurry (which is strange) but the scaled size seems very crisp.
Chrome (on Mac): The original size is crisp but the scaling automatically applies a filter of some sort which makes the resized text instantly blurry.
Safari and Opera (on Mac) are somewhere in between as, the original picture is crisp but after the scaling, they keep the picture crisp for about one second before they apply an anti-aliasing "filter" of some sort.
Those tests on different browsers prove that the blurry render is not a matter of a non integer ratio between the original and scaled picture.
Firefox starts bad but ends good.
Safari/Opera: start good but end bad after a second
Chrome: start good but instantly ends bad.
But it is not a fatality as Firefox shows a decent final scaled picture and Safari/Opera render the scaled picture alright for about one second. Chrome just can't handle it right.
So, these browsers apply something to the pictures and I want that "something" to just not be applied if not asked for, which is a fair request.
So, I tried to use the "image-rendering" property (uncomment it in the css part of the JSfiddle to see it in action).
And it works.
Kind of.
It depends on the picture...
So, it's not a problem solver.
To show that, here is another test where we can see it doesn't work that well in all situations after all:
https://jsfiddle.net/9f36rtza/34/
<style>
.non_scaled_non_filtered{
background:white;
}
.non_scaled_non_filtered img{
height:100%;
}
.scaled_nonfiltered{
margin-top:20px;
background:white;
}
.scaled_nonfiltered img{
height:34px;
}
.scaled_filtered
{
background:white;
margin-top:20px;
image-rendering: -moz-crisp-edges;
image-rendering: -moz-crisp-edges;
image-rendering: -o-crisp-edges;
image-rendering: -webkit-optimize-contrast;
-ms-interpolation-mode: nearest-neighbor;
}
.scaled_filtered img
{
height:34px;
}
</style>
<div class="non_scaled_non_filtered">
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/84.png'>
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/101.png'>
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/115.png'>
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/116.png'>
</div>
<div class="scaled_nonfiltered">
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/84.png'>
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/101.png'>
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/115.png'>
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/116.png'>
</div>
<div class="scaled_filtered">
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/84.png'>
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/101.png'>
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/115.png'>
<img src='http://www.lexgotham.com/xx-prev-xx/b-generator8/test/116.png'>
</div>
Firefox (on Mac): The non-scaled/non-filtered picture is a bit blurry once again, for whatever reason compared to the original (which is supposed to be the same size). But the "image-rendering" gives a scaled result very close to the original. Way more than the non-scaled picture, which is weird.
Chrome and Opera (on Mac):The unscaled picture is crisp as it should and the scaled picture
with "image-rendering is a bit blurry. Less than the scaled/unrendered one, though.
Safari (on Mac): This one is absolutely the worst. The non scaled picture is perfect.
The scaled but unfiltered version is a bit blurry. And the scaled and
"image-rendered" picture is just totally over-processed. It looks
like the picture has been sharpened several times in a row. This alone makes the "image-rendering" property unusable for production.
FYI: In the JSFiddle, I also added the "font-smoothing" and "text-rendering" properties (uncomment if you want to see them in action).
But as we don't deal with real text, of course, it doesn't change a thing.
All in all, it doesn't look like "image-rendering" is de-activating the "anti-aliasing" filter that the browsers apply by default.
It only seems to add a new layer of image rendering process that each browser handles more or less well.
So, it's not a problem solver. It only hides more or less well the problem, depending of the picture. And sometimes it's even worse (Safari).
All I want is to de-activate any unwanted anti-aliasing process for any size of picture and to have a consistent result among all the browsers.
So, I have a situation.
I need to have a faithful render as I'm doing a web app that renders divs containing pictures and texts that people can scale, and the final purpose is to print the results in Hi-res.
That filter applied won't just allow me that and I have found no solution or workaround that works in any situation.
Depending on the browser, people will get various results with none of them that is perfect.
If I don't apply "image-rendering", all aforementioned browsers but Firefox is giving me a blurry render. And firefox gives me a blurry render if the picture is not scaled.
If I do apply "image-rendering" however, none will handle the sharpness well. Firefox is the least worse but it is not perfect neither.
Each of them seems to apply another filter instead of just de-activating that anti-alias filter they just apply.
So, what can I do?
The ideal way would be to know how just not to apply that anti-alias filter in the first place.
Of course I can use other ways to scale down the picture (css scale/zoom) but it's just the same. I tried. The method of scaling doesn't seem to be the problem, it's the way the browsers render a scaled image in all cases.
Thank you for your help.
I have a issue that text is pixelated when 2D scale of CSS transform is applied. (I only tested in Chrome. I'll do cross-browsing later, so you don't need to test it on different browsers.)
CSS
.versus_block_hover
{
-webkit-transform: scale(1.2,1.2)!important;
-webkit-transition: 50ms 0ms!important;
z-index:1100!important;
border-width:1px;
border-color:#000;
border-style:solid;
}
Javascript Code
$('.versus_block').hover(function() {
$(this).addClass('versus_block_hover');
$(this).parent().css('z-index','1100');
}, function() {
$(this).removeClass('versus_block_hover');
$(this).parent().css('z-index','0');
});
I know that Chrome uses bitmap operation during CSS transform for 3D acceleration.
However, when I tested it in jsfiddle, it wasn't pixelated. In face, it seems that Chrome re-draws a text when the transition is done.
See this working example. Hover over a box. (I put all CSS elements from my actual site.)
http://jsfiddle.net/eCkdE/11/
And, this is an actual site that has an issue. (Hover any of blocks, then you can see pixelated fonts when it's expanded)
http://jsfiddle.net/GJ7BM
Anyone has an idea how to fix it? It's okay that you can fix it on my jsfiddle directly.
The problem seems similar to this: http://code.google.com/p/chromium/issues/detail?id=119470
So what actually triggers the problem, is when the element is rendered as a comsposited layer on uneven coordinates. If you enable the Composited render layer borders option in chrome://flags you can see that in your first jsfiddle example the div becomes a composited layer while the transition is in progress, the text becomes blurry, once the transition is complete it's no longer composited, and the text becomes clear (see this modified fiddle where it's easier to spot the border: http://jsfiddle.net/kF2Q5/).
In your second jsfiddle example the text stays blurry even after the transition is complete because it's still an composited layer (on uneven coordinates presumably), this is caused by these lots of nested and underlayed transforms (an element that lies above a composited layer, becomes a composited layer too). See this modified fiddle: http://jsfiddle.net/PqPSU/ All transforms are being disabled using:
* {
-webkit-transform: translateX(0) !important;
}
So only the transform on the hovered element is left. If you have enabled the Composited render layer borders option, you should see that all the red/brown borders around the tiles, indicating composited layers should be gone. And you should see that the text becomes clear once the transition is complete, just like in your first example.
Unfortunately I have no solution for the underlying problem, ie the blurry rendering of composited layers, I guess it's something that cannot be solved on this end, but can only be fixed in the rendering engine and/or the graphics card driver, depending on where exactly the problem is caused.
You can adjust the text-rendering css property.
https://developer.mozilla.org/fr/docs/CSS/text-rendering
But your example works fine for me, check on another computer.
You should update your browser, or your graphic card drivers.
Got a really tricky problem that I've been trying to figure out for a while. Essentially I'm building a large rotating wheel which has multiple elements rotating at different speeds. It's about 5000px square, but the viewport is a percentage of window height. Due to the scaling, the wheel itself is an SVG so that it looks sharp on all devices. The SVG has some pretty complicated paths in places.
It's hard to explain, so I've made a barebones demo of what I'm talking about: http://jsfiddle.net/UsVeZ/3/embedded/result/ (edit it here: http://jsfiddle.net/UsVeZ/13/). Click / tap the document to see it rotate.
Everything looks fine, and it works great on the desktop (Chrome + Safari), but when I rotate the wheel on an iPad, mobile safari lags with the rendering. The rotating animation itself is pretty smooth, but it's just the rendering that can't keep up. It's way worse with my complex SVG (which unfortunately I'm not permitted to share, but the demo gives an idea). Check it out on an iPad to see what I mean.
I'm not quite sure how to solve this problem - ideally it would be great if there was some way to keep the entire SVG in memory (not sure exactly how the rendering works or why it's doing what it's doing). If anyone has any ideas, or even a different way to approach, that would be amazing.
This is not a full answer, just a couple of strategies you could follow.
If you apply...
body {
-webkit-transform: scale(0.3);
}
You will see how Mobile Safari caches your svg, it gets pixelated and then renders correctly. If you tap and wait, it will re-cache the image when you tap again. But if you tap continously, you will notice you don't give it a chance to recache the image.
This happens in your jsfiddle too, if you tap continously eventually you don't get any choppy blocks.
So if you don't let Safari recache the image by creating an endless transition like...
$("svg").on("webkitTransitionEnd", function(){
rotation -= .01;
$("#large").css('-webkit-transform', "translate3d(0, 0, 0) rotate(" + rotation + "deg)");
$("#small").css('-webkit-transform', "translate3d(0, 0, 0) rotate(" + ( rotation * 1.5 ) + "deg)");
});
Then the cache will never go stale.
However, once you remove the scale on the body, it seems that Safari won't cache any graphics outside of the viewport. So, perhaps if you make the wheel have a full spin on load it will cache the image.
So I couldn't end up solving this issue properly on my real example. I ended up reverting to images (I split up my large image into 9 smaller images and tiled them to get around iOS' 5mp limit), and on each image I applied the following property which seems to cache the image:
-webkit-transform-style: preserve-3d;
It doesn't scale as nicely, but it works smoothly which is more important.
I actually took several different SVG's and applied a rotation to them in iOS and it ends up adding this box the instant I rotate it. I can't duplicate it in Safari or Chrome (desktop). Tried a bunch of different things to get it to stop, but no luck. I haven't been able to find much feedback online about if its a bug or not.
Just applying -webkit-transform: rotate(122deg);
Example at - http://bit.ly/QnAgFK
I found that certain [object] tags have there #documents add [html][body] tags around the svg in Safari 6. Others don't. So my assumption is this is a mime type host issue, but I've got the image/svg+xml mime-type added for .svg.
I have a dirty fix for this one.
#topParentId *{
-webkit-transform-style: preserve-3d !important;
}
I have applied css to every child of topParentId Id, and this works fine for me. It changes a little css in output but it works fine for me.
I'm seeing an issue where Chrome and other WebKit browsers massively blur any css-scaled content that also has translate3d applied.
Here's a JS Fiddle: http://jsfiddle.net/5f6Wg/. (View in Chrome.)
.test {
-webkit-transform: translate3d(0px, 100px, 0px);
}
.testInner
{
/*-webkit-transform: scale(1.2);*/
-webkit-transform: scale3d(1.2, 1.2, 1);
text-align: center;
}
<div class="test">
<div class="testInner">
This is blurry in Chrome/WebKit when translate3d and scale or scale3d are applied.
</div>
</div>
Are there any known workarounds for this? I get that in the simple example above, I could simply use translate rather than translate3d - the point here is to strip the code down to the bare essentials.
Webkit treats 3d transformed elements as textures instead of vectors in order to provide hardware 3d acceleration. The only solution to this would be to increase the size of the text and downscaling the element, in essence creating a higher res texture.
See here:
http://jsfiddle.net/SfKKv/
Note that antialiasing is still underpar (stems are lost) so I'm beefing up the text with a bit of text shadow.
I found that using:
-webkit-perspective: 1000;
on the container of your font or icon set kept things crisp for me after experiment with the issue on Android nexus 4.2 for sometime.
A css filter effect is a graphical operation that allows to manipulate the appearance of any HTML element. Since Chromium 19 these filters are GPU accelerates to make them super fast.
CSS3 introduces a bunch of standard filter effects, one of them is the blur fitler:
-webkit-filter: blur(radius);
The ‘radius’ parameter affects how many pixels on the screen blend into each other, so a larger value will create more blur. Zero of course leaves the image unchanged.
Set the radius to 0 will force browser to use GPU calculation and force it to keep your html element unchanged. It's like applying an "hard edges" effects.
So the best solution for me in order to fix this blurry effect was to add this simple line of code:
-webkit-filter: blur(0);
There is also a known bug that only affects retina screens. (See here: Why doesn't blur(0) remove all text blur in Webkit/Chrome on retina screens?). So in order to make it works also for retina, I recommend to add this second line:
-webkit-transform: translateZ(0);
Try this
...{
zoom:2;
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
Or for a more exact approach you can call a javascript function to recalculate the transformation matrix by removing the decimal values of the matrix. see: https://stackoverflow.com/a/42256897/1834212
I came across this issue when using the isotope plugin (http://isotope.metafizzy.co/index.html) in combination with the zoom plugin (http://janne.aukia.com/zoomooz/). I built a jquery plugin to handle my case. I threw it up in a github repo in case anybody could benefit from it. - https://github.com/charleshimmer/jquery-hirestext.
I used javascript to move the text 1px top and then with 100ms after, back 1px down. It's almost unperceptive and solved the problem completely cross-browser.
body {
-webkit-font-smoothing: subpixel-antialiased;
}
or I think you could put that on a specific element, but I was having problems with one element affecting the whole site.
I think it is a problem with custom font-face fonts.
Webkit treats 3d transformed elements as textures instead of vectors
in order to provide hardware 3d acceleration.
This has nothing to do with it. You'll notice that your aliasing problem can be fixed with the addition of duration and direction information (e.g. 0.3 linear). Your having a mare trying to render everything at runtime:
Same for the above ^