如果你看看这种方法的演示,并且在开发者工具中观察,你会发现它的性能更加优异。在这个方法里,我们只需在Canvas上调用drawImage API、设置背景图像,以及每一个要在屏幕上正确位置绘制的色块。 /** * Updates and
如果你看看这种方法的演示,并且在开发者工具中观察,你会发现它的性能更加优异。在这个方法里,我们只需在Canvas上调用drawImage API、设置背景图像,以及每一个要在屏幕上正确位置绘制的色块。 /**
* 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));
}
/**
* 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));
}
这种做法真的在处理大图片(或者其它很容易写到一个Canvas上的元素)或者大块的文本时肯定根据挑战性。但是在你的网站上,它可能被证明会是最合适的 解决方案。如果你不得不在Canvas上处理文本,你也许要使用fillText API,但是它有访问成本(你刚刚才把文本转换为bitmap!)而且你需要处理文本换行以及其它问题。你需要尽量避免这么做。
讨论了这么多,我们没有理由假设视差的工作就一定要用Canvas元素。如果浏览器支持,我们可以使用WebGL。这里面的关键是WebGL是所有API到显卡最直接的方式,并且在你的站点效果很复杂的情况下性能是最有可能达到60fps的。
你最直接的反应可能是觉得采用WebGL矫枉过正,或者它并没有获得广泛支持,但是如果你如果使用了类似于Three.js的库,你可以随时回退为使用 Canvas元素,同时你的代码能以一种一致和友好的方式进行抽象。我们需要做的只是用Modernizr来检测相应API的支持:
// check for WebGL support, otherwise switch to canvas
if (Modernizr.webgl) {
renderer = new THREE.WebGLRenderer();
} else if (Modernizr.canvas) {
renderer = new THREE.CanvasRenderer();
}
// check for WebGL support, otherwise switch to canvas
if (Modernizr.webgl) {
renderer = new THREE.WebGLRenderer();
} else if (Modernizr.canvas) {
renderer = new THREE.CanvasRenderer();
}
然后使用Three.js的API,而不是自己处理上下文。这里有一个支持两种渲染方式的演示。
这种方法的最后一个问题是,如果你并不特别爱好在页面上添加额外的元素,你可以总是在Firefox和Webkit浏览器里使用canvas作为背景元素。很明显,这并不是普遍适用的,所以你应该对此持谨慎态度。
逐步退化
开发者默认采用绝对定位元素而不是其它方法的主要原因可能仅仅简单是浏览器支持的问题。这种方式在一定程度上是错误的,因为对于老旧的浏览器来说,只能提供非常贫乏的渲染体验。即便在现代浏览器中,使用绝对定位也不一定能带来好的性能。
更好的方案是在老旧的浏览器上避免尝试视差效果,仅在最好的浏览器上确保能够用正确的API呈现站点效果。当然,如果你使用了Three.js,你应该能够很容易根据所需要的支持在渲染器之间进行切换。