- 原文地址:Interactive WebGL Hover Effects
- 原文做者:Yuriy Artyukh
本文主要内容是介绍如何经过一些简单的步骤,在图像上实现交互式鼠标悬停效果。css
我很喜欢 WebGL,在本文中,我将介绍如何在掌握着色器基础上作出炫酷效果。我想要重现的这个效果,源自 Jesper Landberg 的网站,他是一个很是酷的家伙,请务必查看一下他网站上的东西。git
让咱们开始吧!让咱们从编写简单的 HTML 开始:github
<div class="item"> <img src="img.jpg" class="js-image" alt=""> <h2>Some title</h2> <p>Lorem ipsum.</p> </div> <script src="app.js"></script>
这个例子再简单不过了!接下来,让咱们来添加些样式,以使其它起来更漂亮:web
全部动画效果将在 Canvas 元素中呈现,同时咱们还须要添加部分 JavaScript 代码。我在这里使用 Parcel,由于学习起来很是简单,我还将在 WebGL 部分中使用 Three.js。canvas
到这里,咱们开始编写 JavaScript 代码,并按照官方文档着手进行基本的 Three.js 设置:api
import * as THREE from "three"; var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 ); var renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); camera.position.z = 5; var animate = function () { requestAnimationFrame( animate ); cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render( scene, camera ); }; animate();
让咱们设置 Canvas 元素的样式:app
body { margin: 0; } canvas { display: block; position: fixed; z-index: -1; // 将其设置为背景 left: 0; // 铺满整个屏幕 top: 0; // 铺满整个屏幕 }
当你完成全部这些操做,就可使用 parcel index.html
运行它。如今,您不会看到太多,到目前为止,它是一个空的 3D 场景。让咱们暂时搁置 HTML,专一于 3D 场景操做。composer
让咱们建立一个带有图像的简单 PlaneBufferGeometry 对象。像这样:dom
let TEXTURE = new TextureLoader().load('supaAmazingImage.jpg'); let mesh = new Mesh( new PlaneBufferGeometry(), new MeshBasicMaterial({map: TEXTURE}) )
如今,咱们将看到如下内容:
显然咱们尚未实现效果,咱们须要追踪鼠标的颜色轨迹。固然,咱们还须要着色器。若是您对着色器感兴趣,或者您可能已经学过一些有关如何放置图像的教程,例如如何将鼠标放在悬停位置上?液体变形效果?
可是咱们有一个问题:咱们只能在上面的示例中在该图像上(和内部)使用着色器。可是效果并不局限于任何图像边界,而是流动的,覆盖整个区域,就像整个屏幕同样。
事实证实 Three.js 渲染器的输出只是另外一幅图像。咱们能够利用它,并在该输出上应用着色器位移!
这是代码的补充部分:
// 设置后期处理 let composer = new EffectComposer(renderer); let renderPass = new RenderPass(scene, camera); // 用图像渲染场景 composer.addPass(renderPass); // 咱们的自定义着色器传递整个屏幕,以替换之前的渲染 let customPass = new ShaderPass({vertexShader,fragmentShader}); // 确保咱们正在渲染它 customPass.renderToScreen = true; composer.addPass(customPass); // 最后真正使用咱们的着色器渲染场景 composer.render() // 而不是之前的 render() // renderer.render(scene, camera);
但整个过程用一句话归纳就是,着色器被应用到了整个屏幕上。
接下来,让咱们完成具备炫酷效果的最终着色器:
// 在鼠标周围留一个小圆圈,并保持必定距离 float c = circle(uv, mouse, 0.0, 0.2); // 获取 3 次纹理,每次具备不一样的偏移量,具体取决于鼠标速度: float r = texture2D(tDiffuse, uv.xy += (mouseVelocity * .5)).x; float g = texture2D(tDiffuse, uv.xy += (mouseVelocity * .525)).y; float b = texture2D(tDiffuse, uv.xy += (mouseVelocity * .55)).z; // 将全部内容合并到最终输出 color = vec4(r, g, b, 1.);
您能够在第一个演示中看到此结果。
屏幕具备不一样尺寸,3D 图像也各自具备尺寸。所以,咱们如今要作的是计算这二者之间的某种关系。
就像我同样吗?在上一篇文章中,咱们能够制做一个宽度为 1 的平面,并将其彻底适配屏幕宽度。因此实际上,咱们使用了: WidthOfPlane=ScreenSize
。
对于咱们的 Three.js 场景,这意味着若是要在屏幕上显示 100px 宽的图像,咱们将建立一个 Three.js 对象,其宽度为 100*(WidthOfPlane/ScreenSize)
。经过这种数学运算,咱们还能够轻松设置一些边距和位置。
页面加载后,我将遍历全部图像,获取它们的尺寸,并将它们添加到个人 3D 世界中:
let images = [...document.querySelectorAll('.js-image')]; images.forEach(image=>{ // 如今,咱们有了图像的大小和左边、上边的位置 let dimensions = image.getBoundingClientRect(); // 隐藏原始图像 image.style.visibility = hidden; // 根据其 HTML 将 3D 对象添加到场景中 createMesh(dimensions); })
如今,制做这个HTML-3D混合结构很是简单。
关于 mouseVelocity
我想补充的是,我用它来改变效果的半径,鼠标移动得越快,半径越大。
要使其可滚动,咱们只须要移动整个场景便可,与滚动屏幕的数量相同。使用我以前提到的相同公式:NumberOfPixels*(WidthOfPlane/ScreenSize)
。
有时,WidthOfPlane
等于甚至更容易 ScreenSize
。这样,您最终在两个世界中获得的数字彻底相同!
使用不一样的着色器,您可使用此方法产生任何效果。所以,我决定使用一些参数。
无需将图像分为三个颜色层,咱们能够根据与鼠标的距离来简单地移动图像:
vec2 newUV = mix(uv, mouse, circle); color = texture2D(tDiffuse,newUV);
对于最后一个效果,我使用了一些随机性,以在鼠标光标周围得到像素化效果。
在最后一个演示中,您能够在效果之间切换以查看能够进行的一些修改。有了“缩放”效果,我只使用了一个位移,可是在最后一个中,我还对像素进行了随机化,这对我来讲看起来很酷!
很高兴看到您对此动画的想法。您将用这种技术产生什么样的效果?
在 GitHub 上找到这个项目。