This article introduces a fashionable website design method and how to build a high-performance site through HTML5 and browser rendering mechanisms from shallow to deep.
This article involves many principles of browser redrawing and performance optimization. It is also an expansion and continuation of "Web Scrolling Performance Optimization Practice". The difficulty level is intermediate and advanced. Please read this article before reading. article.
Introduction
Parallax sites are all the rage lately, just check out these sites:
Old Pulteney Row to the Pole
# Old Pulteney Row to the Pole
#
BBC News - James Bond: Cars, catchphrases and kissesMethod 1: Using DOM elements and absolute positioning
This may be the way most people choose. There are many elements on the page, and when scroll events are triggered, many visual updates will occur on these elements. Here I show a demo page. If you enable frame mode in the Developer Tools timeline and scroll up or down, you will notice expensive full-screen drawing operations. If you scroll multiple times, you may see multiple scroll events in a single frame, each of which triggers layout work. Developer tools show a large number of drawing operations and multiple event-triggered layouts in a frame It is important to remember that in order to achieve At 60fps (matching a typical monitor refresh rate of 60Hz), we have to do everything in almost 16ms. In this first version, every time we get a scroll event, we have to perform a visual update, but as we discussed in the previous articles - "Using requestAnimationFrame to implement simpler animations" and "Web scrolling performance optimization practice" This is inconsistent with the browser's update rhythm. So we either miss frames or get too much work done in one frame. This can easily make your site look uncomfortable and unnatural, leading to user frustration.Let’s move the visual update code from the scroll event to the requestAnimationFrame
callback, and simply get the scroll value in the callback of the scroll event. We demonstrate this change in our second demo. ###If you repeat the rolling test, you may notice a slight improvement, although not much. The reason is that layout operations triggered by scrolling are expensive, and now we only perform layout operations once per frame.
The developer tools show a large number of drawing operations and multiple event-triggered layouts in one frame
We can now process it in each frame One or hundreds of scroll events, but most importantly, we only store the most recent scroll value for use when the requestAnimationFrame callback is triggered and perform visual updates. The key is that we've gone from optimizing visual updates every time we receive a scroll event to doing it at the right moment given to us by the browser. Do you think this is quite powerful?
The main problem with this method is that, whether using requestAnimationFrame or not, we are basically generating layers for the entire page, which requires a lot of and expensive when moving these visual elements. Redraw . Usually redrawing is a blocking operation (although this will be optimized), which means that the browser cannot do other work at the same time, and we often may exceed the browser's 16ms frame processing time limit, which means that there will be performance lags. Dayton situation.
Method 2: Using DOM elements and 3D transformation
In addition to absolute positioning, another method we can use is 3D transformation (transform). In this case we can see that each element processed with 3D Transform results in a new layer. In contrast, in method 1, we have to redraw a large portion of the layer on the page if anything changes.
This means things are very different with this method: we might have a layer for any element that has a 3D transform applied to it. If we do this by transforming more elements, we don't need to redraw any layers and the GPU is able to handle moving elements and compositing the entire page. Maybe you are wondering why 3D transform is used instead of 3D, the reason is that 2D transform does not guarantee to get a new layer, while 3D transform does.
This is another demo using 3D transformation. As you scroll you can see that performance has improved a lot.
Many times people use the -webkit-transform:translateZ(0) technique and can see wonderful performance improvements (Yujie Note: Regarding this method, it actually uses 3D Converting to enable browser hardware acceleration is a kind of Hack that is rarely mentioned in domestic information, but there are many articles on mobile app development performance optimization in China and "Improving HTML5 Web Page Performance" in foreign countries. "IncreasingPerformance of HTML and JavaScript on Mobile Devices"). This method can work normally now, but it will bring some problems:
1. It is not browser compatible;
2. It forces the browser to create a new layer. A large number of layers can create other performance bottlenecks, so they need to be used sparingly.
3. It is disabled on some Webkit version transplants.
Therefore, you need to be very careful if you adopt this method. This is a temporary solution to the problem. In a perfect world we wouldn't even think about it, and with browsers improving every day, who knows maybe one day we won't need it.
Method 3: Use Fixed Position Canvas or WebGL
The last method we need to consider is to use a Fixed Position Canvas on the page, and put The transformed image is plotted above. At first glance, this may not be the most efficient solution, but it has several benefits:
We no longer need a lot of compositing work, Because the page has only one element - Canvas;
We can efficiently process a single bitmap through hardware acceleration;
The Canvas2D API is perfect for us The type of transformations to perform, which means development and maintenance are more manageable.
Using the Canvas element gives us a new layer, but it only has one layer, whereas in method 2 we create a new layer for each element to which 3D transformation is applied, so there is extra work to do These layers are composited together.
If you look at the demonstration of this method and observe it in the developer tools, you will find that its performance is even better. In this method, we only need to call the drawImage API on the Canvas, set the background image, and each color block to be drawn at the correct position on the screen.
/** * Updates and draws in the underlying visual elements to the canvas. */ function updateElements () { var relativeY = lastScrollY / h; // Fill the canvas up context.fillStyle = "#1e2124"; context.fillRect(0, 0, canvas.width, canvas.height); // Draw the background context.drawImage(bg, 0, pos(0, -3600, relativeY, 0)); // Draw each of the blobs in turn context.drawImage(blob1, 484, pos(254, -4400, relativeY, 0)); context.drawImage(blob2, 84, pos(954, -5400, relativeY, 0)); context.drawImage(blob3, 584, pos(1054, -3900, relativeY, 0)); context.drawImage(blob4, 44, pos(1400, -6900, relativeY, 0)); context.drawImage(blob5, -40, pos(1730, -5900, relativeY, 0)); context.drawImage(blob6, 325, pos(2860, -7900, relativeY, 0)); context.drawImage(blob7, 725, pos(2550, -4900, relativeY, 0)); context.drawImage(blob8, 570, pos(2300, -3700, relativeY, 0)); context.drawImage(blob9, 640, pos(3700, -9000, relativeY, 0)); // Allow another rAF call to be scheduled ticking = false; } /** * Calculates a relative disposition given the page’s scroll * range normalized from 0 to 1 * @param {number} base The starting value. * @param {number} range The amount of pixels it can move. * @param {number} relY The normalized scroll value. * @param {number} offset A base normalized value from which to start the scroll behavior. * @returns {number} The updated position value. */ function pos(base, range, relY, offset) { return base + limit(0, 1, relY - offset) * range; } /** * Clamps a number to a range. * @param {number} min The minimum value. * @param {number} max The maximum value. * @param {number} value The value to limit. * @returns {number} The clamped value. */ function limit(min, max, value) { return Math.max(min, Math.min(max, value)); }
This approach is really useful for dealing with large images (or other elements that are easy to write on a Canvas) or large blocks The text is certainly challenging. But on your website, it may prove to be the most appropriate solution. If you have to process text on Canvas, you might want to use the fillText API, but it has an access cost (you just converted the text to a bitmap!) and you need to deal with text wrapping and other issues. You need to try to avoid doing this.
Having discussed so much, we have no reason to assume that the Canvas element must be used to work with parallax. We can use WebGL if the browser supports it. The key here is that WebGL is the most direct way from all APIs to the graphics card, and when your site's effects are complex, the performance is most likely to reach 60fps.
Your immediate reaction may be that WebGL is overkill or that it is not widely supported, but if you use a library like Three.js, you can always fall back to using the Canvas element. At the same time your code can be abstracted in a consistent and friendly way. All we need to do is use Modernizr to detect the support of the corresponding API:
// check for WebGL support, otherwise switch to canvas if (Modernizr.webgl) { renderer = new THREE.WebGLRenderer(); } else if (Modernizr.canvas) { renderer = new THREE.CanvasRenderer(); }
and then use the Three.js API instead of ourselves Processing context. Here is a demo that supports both rendering methods.
The last problem with this approach is that if you don’t particularly like adding extra elements to the page, you can always use canvas as a background element in Firefox and Webkit browsers. Obviously, this isn't universally applicable, so you should be cautious about this.
逐步退化
开发者默认采用绝对定位元素而不是其它方法的主要原因可能仅仅简单是浏览器支持的问题。 This approach is wrong to a certain extent, because for older browsers, it can only provide a very poor rendering experience. Even in modern browsers, using absolute positioning does not necessarily lead to good performance.
A better solution is to avoid trying parallax effects on older browsers, and only use the best browsers to ensure that the correct API can be used to render the site effect. Of course, if you use Three.js, you should be able to easily switch between renderers depending on the support you need.
Conclusion
We evaluated several ways to handle heavily redrawn areas, from absolutely positioned elements to using fixed-positioned Canvases. Of course the approach you take will depend on what you're trying to achieve and your specific design, but it's good to know that there are options. In the example of this article, we managed to optimize from a relatively laggy, sub-30fps to a smooth, 60fps effect.
The above is the detailed content of Detailed explanation of graphic and text code for building a high-performance parallax website using HTML5. For more information, please follow other related articles on the PHP Chinese website!