隨着互聯網的發展,動畫效果也在一直更新,從剛開始的flsh動畫,cocos骨骼動畫,到YY開源的svga動畫。最近1年來,帶有透明通道的mp4動畫被使用的極為廣泛,對於app端。github上有開源的AlphaPlayer可以使用,但是對於web端,相關的資料很少。為了解決這個問題想了很多辦法,提出了使用webm格式。但是最終還是要實現播放mp4格式。
播放mp4格式的透明動畫,需要什么呢。首先,對於web前端來說,webgl應該大家都很少介紹到,更不要說着色器了(shader)和OpenGL ES的語法了。放棄了好幾次,最后看到一個帖子,可以用THREEJS來進行。先上一個圖
可以看到素材中左邊部分使用RGB通道存儲了原透明視頻的Alpha值,右邊部分使用RGB通道存儲了原透明視頻的RGB值,然后在端上通過OpenGL重新將每個像素點的Alpha值和RGB值進行組合,重新得到ARGB視頻畫面,實現透明視頻的動畫效果。
在web端,具體實現。
1.有個video播放器,播放mp4動畫
2.使用threejs和openel把畫面重繪到一個容器里。
1 var lastUpdate;
2 var container; 3 var camera, scene, renderer; 4 var uniforms; 5 6 function init() { 7 8 container = document.getElementById('container'); 9 camera = new THREE.Camera(); 10 camera.position.z = 1; 11 scene = new THREE.Scene(); 12 13 var video = document.getElementById('video'); 14 video.src = "https://devimage.91banban.com/topcard.mp4"; 15 video.play(); 16 video.onended = () => { 17 video.play(); 18 } 19 videoTexture = new THREE.VideoTexture(video); 20 videoTexture.minFilter = THREE.LinearFilter; 21 videoTexture.magFilter = THREE.LinearFilter; 22 videoTexture.format = THREE.RGBAFormat; 23 24 // shader stuff 25 uniforms = { 26 time: { type: "f", value: 1.0 }, 27 texture: { type: "sampler2D", value: videoTexture } 28 }; 29 var material = new THREE.ShaderMaterial({ 30 uniforms: uniforms, 31 vertexShader: `varying vec2 vUv; 32 void main() { 33 vUv = uv; 34 gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 35 }`, 36 fragmentShader: `#ifdef GL_ES 37 precision highp float; 38 #endif 39 40 uniform float time; 41 uniform sampler2D texture; 42 varying vec2 vUv; 43 44 void main( void ) { 45 gl_FragColor = vec4( 46 texture2D(texture, vec2(0.5+vUv.x/2., vUv.y)).rgb, 47 texture2D(texture, vec2(vUv.x/2., vUv.y)).r 48 ); 49 }`, 50 transparent: true 51 }); 52 lastUpdate = new Date().getTime(); 53 var geometry = new THREE.PlaneBufferGeometry(2, 2); 54 var mesh = new THREE.Mesh(geometry, material); 55 mesh.scale.setScalar(0.8); 56 scene.add(mesh); 57 renderer = new THREE.WebGLRenderer({ alpha: true }); 58 renderer.setPixelRatio(window.devicePixelRatio / 1); 59 60 container.appendChild(renderer.domElement); 61 62 document.getElementById('play-button').addEventListener('click', e => { video.play(); }); 63 renderer.setSize(1000, 1100); 64 this.animate(); 65 } 66 67 function animate() { 68 var currentTime = new Date().getTime() 69 var timeSinceLastUpdate = currentTime - lastUpdate; 70 lastUpdate = currentTime; 71 requestAnimationFrame(animate); 72 render(timeSinceLastUpdate); 73 74 } 75 function render(timeDelta) { 76 uniforms.time.value += (timeDelta ? timeDelta / 1000 : 0.05); 77 renderer.render(scene, camera); 78 } 79 80 init();
最終實現的效果圖