This time we are going to bring you a picture particle hourglass animation based on HTML5 Canvas, which mainly breaks a picture into particles and then simulates an hourglass to drop the picture particles down.
Advertisement
textureBefore.minFilter = THREE.LinearFilter; textureAfter.minFilter = THREE.LinearFilter; var material = new BAS.BasicAnimationMaterial({ side: THREE.DoubleSide, vertexColors: THREE.VertexColors, uniforms: { uTime: { type: 'f', value: 0 }, uSize: { type: 'vf2', value: [width, height] }, mapBefore: { type: 't', value: textureBefore }, mapAfter: { type: 't', value: textureAfter } }, vertexFunctions: [BAS.ShaderChunk['ease_quad_in_out'], BAS.ShaderChunk['ease_quad_in'], BAS.ShaderChunk['ease_quad_out']], vertexParameters: '\n uniform float uTime;\n uniform vec2 uSize;\n uniform sampler2D mapBefore;\n uniform sampler2D mapAfter;\n attribute vec4 aPosition;\n const float interval = ' + INTERVAL + ';\n const float durationStart = ' + DURATION_START + ';\n const float durationEnd = ' + DURATION_END + ';\n const float totalTime = durationStart + interval + durationEnd;\n const float speed = 60.;\n const float minWeight = 0.3;\n const float fallSpeed = 4.;\n const float xSpeed = 0.03;\n const float spreadPosition = 0.03;\n ', vertexInit: '\n vec2 texelCoord = (aPosition.xy + uSize / 2.) / uSize;\n vec4 texelBefore = texture2D(mapBefore, texelCoord);\n vec4 texelAfter = texture2D(mapAfter, texelCoord);\n float bottom = aPosition.y - uSize.y * 1.8;\n float time = uTime / 50.;\n float tTime = mod(time, totalTime);\n float doubleTime = mod(time, totalTime * 2.);\n float isReverse = step(totalTime, doubleTime);\n float progress = max(tTime - durationStart, 0.);\n float nProgress = progress / interval;\n float move = progress * speed;\n float weightBefore = pow(1. - texelBefore.r * texelBefore.g * texelBefore.b, 2.) * (1. - minWeight) + minWeight;\n float weightAfter = pow(1. - texelAfter.r * texelAfter.g * texelAfter.b, 2.) * (1. - minWeight) + minWeight;\n float order = pow(abs(aPosition.x) / (uSize.x * 0.5), 2.) * 40.;\n float fall = max(-aPosition.y - uSize.y / 2. + move - order, 0.) * (aPosition.w * 0.2 + 1.) * (0.3 + nProgress) * fallSpeed;\n float y = aPosition.y - fall * mix(weightBefore, weightAfter, easeQuadIn(min(fall, -bottom) / -bottom)) - move + order * clamp(progress, 0., 1.);\n float offsetY = easeQuadOut(clamp(tTime / durationStart, 0., 1.)) * uSize.y * 0.9;\n float endOffsetY = easeQuadIn(clamp((tTime - (durationStart + interval)) / durationEnd, 0., 1.)) * uSize.y * 0.9;\n ', vertexPosition: '\n transformed.x += aPosition.x / (1. + fall * xSpeed * max(1. - max(-y + (bottom * (1. - spreadPosition)), 0.) / (-bottom * spreadPosition), 0.));\n transformed.y += max(y, bottom) + offsetY + endOffsetY;\n transformed.z += aPosition.z;\n ', vertexColor: '\n vec4 colorBefore = texelBefore * (1. - isReverse) + texelAfter * isReverse;\n vec4 colorAfter = texelBefore * isReverse + texelAfter * (1. - isReverse);\n vColor = mix(colorBefore.rgb, colorAfter.rgb, smoothstep(-uSize.y / 2., bottom, y));\n ' }); material.uniforms['mapBefore'].value.needsUpdate = true; material.uniforms['mapAfter'].value.needsUpdate = true;
html, body { height: 100%; } body { overflow: hidden; margin: 0; background-color: #000; } canvas { position: absolute; top: 0; left: 50%; -webkit-transform: translate(-50%, 0); transform: translate(-50%, 0); }
Advertisement