- 原文地址: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 上找到這個項目。