Why is HTML5 canvas leaving artifacts only in webkit browsers? - javascript

I have some code where I add a canvas on the fly to a draggable, resizable div element. Its too much to paste here -->here is the jsFiddle...
It works fine in IE-10 and FF-19, but in webkit (Chrome-25) there are severe artifacts that get left behind when you drag the div AFTER doing a resize. Here is the test to run:
How to see the artifacts in WEBKIT BROWSERS ONLY
1. Drag the gray header, shake mouse, notice NO artifacts as you shake the div
2. Drop the header, resize div using the lower right handle so that you see about an equal amount of green and purple
3. Repeat step 1, only this time artifacts are everywhere in webkit browsers (not IE 10 or FF 19)
4. Now delete the javascript code from line 11 on down (the function and call) and run fiddle again
5. Repeat the test in steps 1-3. Without the canvas, there are NO artifacts.
6. Try in non-webkit browsers. NO artifacts.
The test case I constructed seems to point to the canvas being the culprit. I don't see this as a coding error since non-webkit works OK. Also, I thought maybe hardware was the issue but I have rebooted / run this on multiple machines and it is repeatable.
Have I found a browser bug, or does someone see an error in my code?

I had the same bug (and yes Sebas, I would call this a bug; better if you had a solution instead of just a critique).
The "overflow: hidden;" in your css is the trigger for the bug.
#wrapper {position: relative; width: 300px; height: 300px; background-color: silver;border: 6px solid lightgray; z-index: 2; border-radius: 15px; overflow: hidden;}
Here is a fork of your jsFiddle with it removed.
http://jsfiddle.net/sv9yZ/1/
I know this is old, figured I'd add this for anybody coming from a search...

I can repeat it in Chrome 25 on Mac OS X 10.8.2.
I've had problems with this before also. There seem to be bugs with the way the canvas drawing routines interact with CSS drawing. As it's a browser bug there isn't a nice way to fix this that I've found. I think all you can do is try hacks like cover the entire background with a div that you flash on and off when dragging to force a redraw. It's not pretty, but I've never found a nicer way to do this.

Related

Subpixel scroll issue, can't set scrollTop properly on Chrome 69

I'm trying to set scrollTop property of some DOM element programmatically and I have weird behaviour that breaks my tests in some specific environment. I created minimal repro (the link)
HTML
<div id=viewport><div id=content></div></div>
CSS
#viewport {
overflow-y: auto;
height: 20px;
}
#content {
height: 150px;
}
JS
const viewport = document.getElementById("viewport");
viewport.scrollTop = 75;
console.log(viewport.scrollTop);
The result of the script is broken on Chrome 69.0.3497.100 running on Win 10 Pro. It is 74.4000015258789 instead of 75. It works properly on the same Chrome version on Mac and even on Win 10 Home running under VirtualBox. Firefox and Edge also have no such an issue.
I know it looks very strange, but what could it be? Can anyone confirm this problem? And could it be fixed in some way to make sure that the result of the scrollTop assignment is exactly I want it to be?
Update. Thanks to #khajjit, I was able to reproduce the issue on my Mac machine and got 74.66666412353516 on 75% zoom out and 150% zoom in. 80% gives 75, 90% -- 74.44444274902344, 110% -- 74.54545593261719 etc (I updated the demo to show the results table). So the issue does not relate to OS. But it looks like the Chrome only issue. Firefox and Edge return 75 at any scale.
Update 2. Scaling the screen resolution at the OS layer also affects the situation. That was the case of Win 10 Pro described above; it had 125% OS scaling.
So the practical part of the question is how to overcome Chrome scaling rounding issues to be able to set scrollTop precisely?
Update 3. Chromium Dev confirmed that this is a bug:
This issue is because Chrome does not fully support fractional scroll
offset.
So if someone is interested in this problem resolving, please follow the link and star the issue on the Chromium end.
I think it might be related to the resolution of the monitor.
I have a resolution of 1920x1080 and with a scale of 100% the result is 75.
But if you set 75% or 150%, the result will be 74.66666412353516.
Or say 25%, the result will be exactly 74.
I don't have any exact answer as to what it might be related to, but I think this little research might lead to the right path.

Asynchronous scrolling (APZ) in Edge janky & problematic; not in IE or other browsers

The Edge browser causes janking of certain divs when they are adjusted by a scroll listener. This janking does not occur in IE, Chrome, Opera, or Firefox.
Even though Firefox doesn't cause janking, it raises a flag in the console when it detects the scroll listener is first employed, and it directs me to this page for explication:
https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects
The article describes what seems to be an exact desciption of the issue I'm having with Edge:
In the asynchronous scrolling model, the visual scroll position is
updated in the compositor thread and is visible to the user before the
scroll event is updated in the DOM and fired on the main thread. This
means that the effects implemented will lag a little bit behind what
the user sees the scroll position to be. This can cause the effect to
be laggy, janky, or jittery — in short, something we want to avoid.
It only offers two solutions, the first of which (position: sticky) only has limited browser support, and the second of which (scroll snapping) has been removed from web standards.
Reaching further, I discovered a good article on APZ (Asynchronous Panning & Zooming) in Edge by its development team:
https://blogs.windows.com/msedgedev/2017/03/08/scrolling-on-the-web/#ahrEuFu6fybJ1uwj.97
It would be ideal if there were a way to turn APZ on and off, but I can't find a way around it. However Firefox has implemented it poses no problem for my routine, but Edge's implementation is extremely problematic, especially since it provides no support for position: sticky.
Is there another way around this issue? It is a problem.
You can force edge to turn off it's APZ implementation by wrapping the scrollable element inside another, forcing that wrapper element into it's own layer, and then finally adding in another dummy, position fixed element, inside that wrapper.
Your HTML would be like so:
// Wrapper element, forced into it's own layer with translateZ.
<div id="wrapper_element" style="transform: translateZ(0);">
// Dummy position fixed element to force APZ off.
<div style="top: 0px; width: 1px; height: 1px; position: fixed; z-index: 1;"></div>
// Your current scrollable element.
<div id="your_scrollable_element">
// Your content
</div>
</div>
This should fix your problem.

Box Shadow disappearing on a div, when deleting list elements inside it

To test or see the bug:
(Note: Bug has been replicated on the link in Second Update, since the question was first posted)
go to sukritchhabra.com/importr
Type Bootstrap in the search bar.
Select bootstrap from the suggested list (Note: If you do not select from the list but press enter in the search box, the page will break and you'll have to refresh)
Once Bootstrap loads, press the green button 6-7 times (until a scroll bar appears)
Now delete a few of them, and the box-shadow will disappear.
I've tried a couple of things so far. Tried logging the box-shadow property of .importrLinks after every delete to catch where it is changing but no change is actually happening.
Also tried explicitly assigning box-shadow after each delete, and that doesn't help either.
During my search for a solution, alot of somewhat similar bugs were because of the z-index but I tried assigning custom z-index's and that didn't help (although I still believe this is something I haven't tested completely since I was assigning random, i.e., altering higher and lower, z-index's just to detect changes).
UPDATE
As suggested in the comments, I should've provided sample code instead of the complete website and I agree. But, as I have mentioned in the comments, I wasn't able to replicate the bug, on a fiddle.
Nevertheless, here is a link to the jsfiddle: https://jsfiddle.net/sukritchhabra/d3xfyc6t/5/
The bug still doesn't take place in the fiddle but, is still there on the website. The code I've used to create the fiddle is picked from the website. Instead of getting arguments for the addLink function, I've just passed constant strings for testing.
Second Update (Bug Replicated)
Figured out the bug is happening because the container has a float: left;. Have changed it on the fiddle and the bug is now replicated on this fiddle.
Link to fiddle: https://jsfiddle.net/sukritchhabra/d3xfyc6t/6/
Third Update (OS issue?)
I had been working on a Mac so far. I just tested it on a Windows machine, and the bug seems to be only on Mac. Am not a 100% sure if that is the root cause, but I'll definitely be testing it on other machines just to be sure.
Meanwhile, if anybody can see the bug on a Mac and not Windows, please do let me know here.
Confirmed the bug in Chrome on OS X on your fiddle. I checked and the same seems to happen to other CSS properties, not just box-shadow. For example, if set, border is affected in the same way and disappears when you remove items. I think it's got something to do with the combination of CSS properties on the container when overflow is set to auto. So for example I noticed that if you remove float: left, box-shadow stops disappearing. So to me it seems to be a browser rendering issue, I'm not sure why it's happening only on OS X.
Anyway, if you need to keep CSS exactly as it is, here is a solution that is far from elegant, but it works:
https://jsfiddle.net/d3xfyc6t/8/
It involves doing a browser repaint on the container every time you remove an element:
$('.importrLinks').hide().show(0);
This technique is taken from here:
https://stackoverflow.com/a/8840703
Oddly, if you remove the float on the container DIV, it fixes the problem: https://jsfiddle.net/d3xfyc6t/10/
You should also add the box-shadow vendor prefixes for other browsers:
box-shadow: 0 0 10px 1px #e2e2e2;
-moz-box-shadow: 0 0 10px 1px #e2e2e2;
-webkit-box-shadow: 0 0 10px 1px #e2e2e2;
Edit: Mimic a left float by replacing float: left with position: absolute; left:0; this gets rid of the problem. You may also need to set any outer containers that contain the code you posted to position: relative; https://jsfiddle.net/d3xfyc6t/11/

Absolute Positioned Floating Header Jitters in Safari

I have a simple JSFiddle of a single floating header here:
http://jsfiddle.net/zT9KQ/
Basically, this uses translate3d to kick in the GPU and hardware accelerate the floating header so that it may be drawn more smoothly. The header jitters in the latest Safari but gets drawn perfectly fine in the latest Chrome, FF and Opera. The actual code that is being affected by this (the code that spurred this question) is code I've written that cannot be shared publicly but works in a similar manner where fixed positioning is, unfortunately, not a valid solution.
I have tried:
Setting the backface-visibility CSS property to none.
Setting the perspective CSS property to 1000.
Playing with requestAnimationFrame during the animation logic.
Throttling the scroll event callback.
Setting the translateZ transform to something higher than 0px.
But none of this has worked (or at least - it seems reasonable to assume the GPU has kicked in but the jittering persists). I noticed that two questions are already open that are identical this one I'm raising but no one has answered them:
Jitter in Sticky Header in Safari
Implementing fixed position in javascript causes jitter in Safari when scrolling
Is this a known bug? Is there a performance hole I'm not sealing up?
EDIT
I have been receiving a lot of questions as to why position: fixed is not a valid option. To directly reply to Antony's comment on the question itself:
I'm not emulating/reinventing position: fixed. If you look at the top-voted answer (as of this comment), you will see that this seems to be a Safari issue. The reason position: fixed is undesirable in this case is because the code in question must be able to support multiple floating headers that sit below each other and have a "container" range where there may be infinitely nested containers. Using fixed positioning not only makes the code more complicated in the case where these floating headers live in a container that horizontally scrolls but also makes the component more brittle overall (calculating offsets when the widget needs to sit within another container somewhere else on the page). So, semantically, absolute positioning fits my needs better than fixed.
SECOND EDIT
Upon thinking about what Antony had been telling me (that I may be reinventing the wheel), and after hearing about -wekbit-sticky from user3716477, I would like to update the question to show what I'm trying to do. You can see how my code behaves in every browser other than Safari here:
http://cl.ly/3y1i3C473G2G
I have learned:
You cannot rely on the scroll or really any scroll-like events (such as mousewheel) since they are asynchronous in nature. I submitted a bug to Apple detailing what was happening and they closed the bug for this reason.
There is no real way to do what I want as of now - to have multiple floating headers that stack and replace each other. I guess I'll have to wait for something like -webkit-sticky to come out.
I should include all relevant information in SO questions from here on out. :-P
Thanks for playing guys! Here's the exact response I received from Apple:
Apple Developer Relations09-Jun-2014 01:16 PM
Engineering has determined that there are no plans to address this
based on the following:
Code is using scroll events, which are asynchronous.
We are now closing this bug report.
If you have questions regarding the resolution of this issue, please
update your bug report with that information.
Please be sure to regularly check new Apple releases for any updates
that might affect this issue.
Since there is an apparent delay between scrolling with the trackpad and the scroll event firing, you can attach the handler to an additional mousewheel event to smoothen things up.
$scrollContainer.on('scroll mousewheel', function () {
// reinvent the wheel here
});
You can see in this demo here that jittering is far less likely to occur when you scroll with the trackpad. In the demo, I have invoked the handler on load to eliminate the flash when you first scroll on Safari. There may still be some occasional jitter, but if you want to minimize that, you can go the resource intensive way of using setInterval and requestAnimationFrame.
This might fix the problem for now, but as I have said before, this emulation approach is not ideal and you are very likely to run into more trouble down the road.
It appears to be a bug with Safari scrolling. If you drag the scrollbar manually (don't use the trackpad gesture to scroll), then there is no jittering.
Chrome (and other browsers) handle scrolling differently which is why this bug is only present on Safari. You might want to submit a bug report to Apple.
How about a little restructuring, like this:
http://jsfiddle.net/me2loveit2/zT9KQ/6/
html:
<div>
<h1>Header</h1>
<div class="container">
<div class="content"></div>
</div>
</div>
CSS:
.container {
height: 300px;
position: relative;
overflow-y:scroll;
}
.content {
height:1000px;
}
h1 {
position: relative;
top:0px;
left:0px;
margin: 0;
width: 100%;
background: black;
color: white;
}
If you are using something similar to the jiddle code you have to check if browser is safari and then make it a different way like:
$(function () {
var $header = $('h1'),
$container = $('.container'),
$scrollContainer = $('.scroll-container'),
scrollContainerOffset = $scrollContainer.offset().top;
$scrollContainer.on('scroll', function () {
var top = Math.max(0, $container.offset().top * -1 + scrollContainerOffset);
$header.css('top', top + 'px');
});
});
Even when it isn't the way you want your problem to be solved, it might be a workaround that may help...
Scroll events are sent asynchronously in various browsers; you should not rely on them to do things like this.
The best solution would be to use position:-webkit-sticky; top: 0;
You could try and disable all and any javascript-ish scroll handling and just simply remove
position: relative; from the .container.
Afterwards just add h1{ top:0; } and it will happily stick to the .scroll-container.
Excerpt from CSS absolute positioning:
Position it at a specified position relative to its closest positioned ancestor...
To explain - your absolute H1 will look up the tree for the first ancestor element which defines a "position" property and inherit it as a 0,0 reference point.
It could be a WTF at first, but this behaviour is a powerhouse once you tame it.
EDIT: Related to the original jsFiddle, I made some more property removals:
http://jsfiddle.net/253Ss/
^ .container wrapper could be removed as well, since it is no more neccessary in a technical sense.

vertical scroll bar not working when using window xp theme (not classic)

This may sound crazy, and i didnt believe it until i saw it for myself.
The vertical scroll bar does not scroll when you click in the space between the scroller or the arrows. You have to drag the bar to get it to scroll. This only happens in the ugly default theme (not windows classic).
The scroll bar has some heavy javascript behind it that drive scrolling of another DIV on the page.
Has anyone even encountered this before? Why the heck does the theme influence IE's rendering?! Only IE!
edit:
this problem happens when you are in "non-classic" XP theme (the default ugly one). switching themes does not cause this, but it does fix it if you switch to classic from the default.
Does it help when you add
<!--[if IE]><meta http-equiv="MSThemeCompatible" content="no"><![endif]-->
to your page source? It worked well to work around glitches in IE when a non-classic theme is enabled for me. Note that the conditional comments are there so that firefox does not parse the tag (because it will screw up scrollbars in firefox sometimes).
I just had the same problem with vertical scrollbar in IE7 within the XP theme. After many experiments, I finally found the solution to it. I don't know this solution fits your case.
The container (div#scroll in the example below) with the rendered scrollbars must be larger than 18px. Any number below that will cause the vertical bar disabled.
<div id="scroll">
<div id="fill">
</div>
</div>
The stylesheet, for example:
#fill{
width: 1px;
height: 1000px;
}
#scroll {
width: 18px; /* This has to be larger than or equal to 18. */
height: 50px;
overflow-y: scroll;
}
Thanks,
Grace
In order to avoid certain Windows restrictions, IE re-implements almost all the controls visible on a web page.
The Old New Thing : Windowless controls are not magic
<excerpt>
The Internet Explorer team went and reimplemented all of the controls that a web page would need. They have their own windowless checkbox control, a windowless listbox control,
a windowless edit box, and so on. In addition to reproducing all the functionality of the windowed controls, the Internet Explorer folks also had to reproduce the "look" of the
windowed controls, down to the last pixel. (Functions like
DrawThemeBackground and DrawFrameControl prove extremely helpful here.)
If I recall correctly, the only element that is still windowed is the <SELECT> element.
If you squint, you can see some places where they didn't quite nail it. For example, if you right-click in a text box, options like "Right to left reading order" and "Insert Unicode control character" are missing. As another example, notice that IE's scroll bars do not light up when you hover over them.
</excerpt>
Without seeing the code implementing your page, I would guess that this is another manifestation of "IE's controls don't act native".
If you have a support contract with Microsoft, I'd suggest complaining to them through that channel; if not, see if you get any responses in the microsoft.public.internetexplorer.general newsgroup. Back in IE6, they broke the scrollbar with KB824145 and fixed it with KB832894, so it's not like theses sorts of problems haven't happened and been resolved before.
First, I'll start saying that IE is a terrible browser and in some cases you don't have control over issues while developing a web application.
In that case I haven't encounter such an issue and a Google search don't pull anything on the subject. I'd say it must be something in your code that lead to a weird bug. Most likely it won't break in a lot of systems, so unless it's a very important application (sales, government...) you could leave it that way.
Look at it that way: who change the style of windows to classic? Geeks. And do geeks use IE? No. ^^ ... Joking, but not really.
If it's important I'll need to take a look at your actual code for "The scroll bar has some heavy javascript behind it that drive scrolling of another DIV on the page."

Categories

Resources