





网页中的视差效果是指,页面中不同元素以不同的速度运动,形成z方向上接近3D的视觉感受。这里是响应鼠标位置的视差效果的具体实现,运动元素各自设置不同的深度值,对鼠标位置作不同程度的响应。
HTML结构如下,运动元素用data-depth设置不同的深度值。
<div class="scene">    <div data-depth="1.0"><img src="layer1.png"></div>    <div data-depth="0.8"><img src="layer2.png"></div>    <!-- 省略... -->    <div data-depth="0.0"><img src="layer6.png"></div></div>视差引擎类,指定运动场景,设置响应鼠标的窗口尺寸,绑定事件,使用requestAnimationFrame更新动画。
class Parallax {    constructor(element, options) {        this.element = element        this.elementWidth = 0        this.elementHeight = 0        this.depthsX = []        this.depthsY = []
        const DEFAULTS = {            frictionX: 0.1,            frictionY: 0.1,            originX: 0.5,            originY: 0.5        }
        Object.assign(this, DEFAULTS, options)
        this.inputX = 0        this.inputY = 0
        this.velocityX = 0        this.velocityY = 0
        this.windowWidth = 0        this.windowHeight = 0        this.windowCenterX = 0        this.windowCenterY = 0        this.windowRadiusX = 0        this.windowRadiusY = 0
        this.raf = null
        this.onWindowResize = this.onWindowResize.bind(this)        this.onMouseMove = this.onMouseMove.bind(this)        this.onAnimationFrame = this.onAnimationFrame.bind(this)
        this.initialise()    }
    initialise() {        helpers.accelerate(this.element)
        let style = window.getComputedStyle(this.element)        if (style.getPropertyValue('position') === 'static') {            this.element.style.position = 'relative'        }        this.element.style.pointerEvents = 'none'
        this.updateLayers()        window.onload = () => {            this.updateDimensions()        }
        window.addEventListener('resize', this.onWindowResize)        window.addEventListener('mousemove', this.onMouseMove)        this.raf = requestAnimationFrame(this.onAnimationFrame)    }
    updateLayers() {        this.layers = this.element.children
        for (let index = 0; index < this.layers.length; index++) {            let layer = this.layers[index]
            helpers.accelerate(layer)
            layer.style.position = index ? 'absolute' : 'relative'            layer.style.display = 'block'            layer.style.left = 0            layer.style.top = 0
            let depth = helpers.data(layer, 'depth') || 0            this.depthsX.push(helpers.data(layer, 'depth-x') || depth)            this.depthsY.push(helpers.data(layer, 'depth-y') || depth)        }    }
    updateDimensions() {        this.windowWidth = window.innerWidth        this.windowHeight = window.innerHeight        this.windowCenterX = this.windowWidth * this.originX        this.windowCenterY = this.windowHeight * this.originY        this.windowRadiusX = Math.max(this.windowCenterX, this.windowWidth - this.windowCenterX)        this.windowRadiusY = Math.max(this.windowCenterY, this.windowHeight - this.windowCenterY)
        const bounds = this.element.getBoundingClientRect()        this.elementWidth = bounds.width        this.elementHeight = bounds.height    }
    setPosition(element, x, y) {        x = x.toFixed(1) + 'px'        y = y.toFixed(1) + 'px'        helpers.css(element, 'transform', 'translate3d(' + x + ',' + y + ',0)')    }
    onWindowResize() {        this.updateDimensions()    }
    onMouseMove(event) {        let clientX = event.clientX,            clientY = event.clientY
        if (this.windowRadiusX && this.windowRadiusY) {            this.inputX = (clientX - this.windowCenterX) / this.windowRadiusX            this.inputY = (clientY - this.windowCenterY) / this.windowRadiusY        }    }
    onAnimationFrame() {        this.velocityX += (this.inputX * this.elementWidth * 0.1 - this.velocityX) * this.frictionX        this.velocityY += (this.inputY * this.elementHeight * 0.1 - this.velocityY) * this.frictionY        for (let index = 0; index < this.layers.length; index++) {            let layer = this.layers[index],                xOffset = -this.velocityX * this.depthsX[index],                yOffset = -this.velocityY * this.depthsY[index]            this.setPosition(layer, xOffset, yOffset)        }        this.raf = requestAnimationFrame(this.onAnimationFrame)    }}实例化,指定响应场景元素。
const scene = document.querySelector('.scene')new Parallax(scene)除了PC端的鼠标事件,也可监听移动设备方向和运动相关的事件deviceorientation和devicemotion。