共计 3334 个字符,预计需要花费 9 分钟才能阅读完成。
- 原文地址:Interactive WebGL Hover Effects
- 原文作者:Yuriy Artyukh
本文次要内容是介绍如何通过一些简略的步骤,在图像上实现交互式鼠标悬停成果。
查看 Demo 或 下载源码
我很喜爱 WebGL,在本文中,我将介绍如何在把握着色器根底上做出炫酷成果。我想要重现的这个成果,源自 Jesper Landberg 的网站,他是一个十分酷的家伙,请务必查看一下他网站上的货色。
让咱们开始吧!让咱们从编写简略的 HTML 开始:
<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>
这个例子再简略不过了!接下来,让咱们来增加些款式,以使其它起来更丑陋:
所有动画成果将在 Canvas 元素中出现,同时咱们还须要增加局部 JavaScript 代码。我在这里应用 Parcel,因为学习起来非常简单,我还将在 WebGL 局部中应用 Three.js。
到这里,咱们开始编写 JavaScript 代码,并依照官网文档着手进行根本的 Three.js 设置:
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 元素的款式:
body {margin: 0;}
canvas {
display: block;
position: fixed;
z-index: -1; // 将其设置为背景
left: 0; // 铺满整个屏幕
top: 0; // 铺满整个屏幕
}
当你实现所有这些操作,就能够应用 parcel index.html
运行它。当初,您不会看到太多,到目前为止,它是一个空的 3D 场景。让咱们临时搁置 HTML,专一于 3D 场景操作。
让咱们创立一个带有图像的简略 PlaneBufferGeometry 对象。像这样:
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 上找到这个我的项目。