python gooey

View demo 查看演示Download Source 下载源

Flash’s grandson, WebGL has become more and more popular over the last few years with libraries like Three.js, PIXI.js or the recent OGL.js. Those are very useful for easily creating a blank board where the only boundaries are your imagination. We see more and more, often subtle integration of WebGL in an interface for hover, scroll or reveal effects. Examples are the gallery of articles on Hello Monday or the effects seen on cobosrl.co.

Flash的孙子WebGL在过去几年中越来越流行,它具有Three.js,PIXI.js或最近的OGL.js之类的库。 这些对于轻松创建空白板非常有用,其中只有您的想象力。 我们看到越来越多的WebGL通常会微妙地集成到界面中,以进行悬停,滚动或显示效果。 例如,您好星期一的文章集或在cobosrl.co上看到的效果。

In this tutorial, we’ll use Three.js to create a special gooey texture that we’ll use to reveal another image when hovering one. Head over to the demo to see the effect in action. For the demo itself, I’ve created a more practical example that shows a vertical scrollable layout with images, where each one has a variation of the effect. You can click on an image and it will expand to a larger version while some other content shows up (just a mock-up). We’ll go over the most interesting parts of the effect, so that you get an understanding of how it works and how to create your own.

在本教程中,我们将使用Three.js创建特殊的粘稠纹理,将其用于在悬停时显示另一幅图像。 前往演示观看效果。 对于演示本身,我创建了一个更实际的示例,该示例显示了带有图像的垂直可滚动布局,其中每个图像都有不同的效果。 您可以单击图像,它将扩展为更大的版本,同时显示一些其他内容(只是一个模型)。 我们将介绍该效果中最有趣的部分,以便您了解其效果以及如何创建自己的效果。

I’ll assume that you are comfortable with JavaScript and have some knowledge of Three.js and shader logic. If you’re not, have a look at the Three.js documentation or The Book of Shaders, Three.js Fundamentals or Discover Three.js.

我假设您对JavaScript熟悉并且对Three.js和着色器逻辑有一些了解。 如果不是,请查看Three.js文档或The Shader of Shaders , Three.js基础知识或Discover Three.js 。

Attention: This tutorial covers many parts; if you prefer, you can skip the HTML/CSS/JavaScript part and go directly go to the 注意:本教程涵盖了许多部分。 如果愿意,可以跳过HTML / CSS / JavaScript部分,直接转到“shaders section.着色器”部分。

Now that we are clear, let’s do this!

现在我们很清楚了,让我们开始吧!

在DOM中创建场景 (Create the scene in the DOM)

Before we start making some magic, we are first going to mark up the images in the HTML. It will be easier to handle resizing our scene after we’ve set up the initial position and dimension in HTML/CSS rather than positioning everything in JavaScript. Moreover, the styling part should be only made with CSS, not JavaScript. For example, if our image has a ratio of 16:9 on desktop but a 4:3 ratio on mobile, we just want to handle this using CSS. JavaScript will only get the new values and do its stuff.

在开始制作魔术之前,我们首先要标记HTML中的图像。 在HTML / CSS中设置了初始位置和尺寸之后,比在JavaScript中放置所有内容更容易处理调整场景大小。 此外,样式部分应仅使用CSS而不是JavaScript制成。 例如,如果我们的图片在桌面上的比例为16:9,而在移动设备上的比例为4:3,我们只想使用CSS来处理。 JavaScript将仅获取新值并执行其工作。

// index.html<section class="container"><article class="tile"><figure class="tile__figure"><img data-src="path/to/my/image.jpg" data-hover="path/to/my/hover-image.jpg" class="tile__image" alt="My image" width="400" height="300" /></figure></article>
</section><canvas id="stage"></canvas>
// style.css.container {display: flex;align-items: center;justify-content: center;width: 100%;height: 100vh;z-index: 10;
}.tile {width: 35vw;flex: 0 0 auto;
}.tile__image {width: 100%;height: 100%;object-fit: cover;object-position: center;
}canvas {position: fixed;left: 0;top: 0;width: 100%;height: 100vh;z-index: 9;
}

As you can see above, we have create a single image that is centered in the middle of our screen. Did you notice the data-src and data-hover attributes on the image? These will be our reference images and we’ll load both of these later in our script with lazy loading.

如您在上方看到的,我们已经创建了一个位于屏幕中间居中的图像。 您是否注意到图像上的data-srcdata-hover属性? 这些将是我们的参考图像,稍后我们将通过延迟加载在脚本中加载这两个图像。

Don’t forget the canvas. We’ll stack it below our main section to draw the images in the exact same place as we have placed them before.

不要忘记画布。 我们将其堆叠在主要部分的下方,以在与之前放置的位置完全相同的位置绘制图像。

演示地址

用JavaScript创建场景(Create the scene in JavaScript)

Let’s get started with the less-easy-but-ok part! First, we’ll create the scene, the lights, and the renderer.

让我们从不那么容易但还可以的部分开始吧! 首先,我们将创建场景,灯光和渲染器。

// Scene.jsimport * as THREE from 'three'export default class Scene {constructor() {this.container = document.getElementById('stage')this.scene = new THREE.Scene()this.renderer = new THREE.WebGLRenderer({canvas: this.container,alpha: true,})this.renderer.setSize(window.innerWidth, window.innerHeight)this.renderer.setPixelRatio(window.devicePixelRatio)this.initLights()}initLights() {const ambientlight = new THREE.AmbientLight(0xffffff, 2)this.scene.add(ambientlight)}
}

This is a very basic scene. But we need one more essential thing in our scene: the camera. We have a choice between two types of cameras: orthographic or perspective. If we keep our image flat, we can use the first one. But for our rotation effect, we want some perspective as we move the mouse around.

这是一个非常基本的场景。 但是我们需要场景中的另一项重要内容:相机。 我们有两种类型的相机可供选择:正交相机或透视相机。 如果我们保持图像平坦,则可以使用第一个图像。 但是为了获得旋转效果,我们希望在移动鼠标时有一些透视图。

In Three.js (and other libraries for WebGL) with a perspective camera, 10 unit values on our screen are not 10px. So the trick here is to use some math to transform 1 unit to 1 pixel and change the perspective to increase or decrease the distortion effect.

在带有透视相机的Three.js(和其他用于WebGL的库)中,屏幕上的10个单位值不是10px。 因此,这里的技巧是使用一些数学运算将1单位转换为1像素,并更改视角以增加或减少失真效果。

// Scene.jsconst perspective = 800constructor() {// ...this.initCamera()
}initCamera() {const fov = (180 * (2 * Math.atan(window.innerHeight / 2 / perspective))) / Math.PIthis.camera = new THREE.PerspectiveCamera(fov, window.innerWidth / window.innerHeight, 1, 1000)this.camera.position.set(0, 0, perspective)
}

We’ll set the perspective to 800 to have a not-so-strong distortion as we rotate the plane. The more we increase the perspective, the less we’ll perceive the distortion, and vice versa.

我们将透视图设置为800,以便在旋转平面时不会产生太大的变形。 我们增加的视角越多,我们对扭曲的感知就越少,反之亦然。

The last thing we need to do is to render our scene in each frame.

我们需要做的最后一件事是在每个帧中渲染场景。

// Scene.jsconstructor() {// ...this.update()
}update() {requestAnimationFrame(this.update.bind(this))this.renderer.render(this.scene, this.camera)
}

If your screen is not black, you are on the right way!

如果您的屏幕不是黑色,则说明方法正确!

用正确的尺寸建造飞机 (Build the plane with the correct sizes)

As we mentioned above, we have to retrieve some additional information from the image in the DOM like its dimension and position on the page.

如上所述,我们必须从DOM中的图像中检索一些其他信息,例如图像的尺寸和在页面上的位置。

// Scene.jsimport Figure from './Figure'constructor() {// ...this.figure = new Figure(this.scene)
}
// Figure.jsexport default class Figure {constructor(scene) {this.$image = document.querySelector('.tile__image')this.scene = scenethis.loader = new THREE.TextureLoader()this.image = this.loader.load(this.$image.dataset.src)this.hoverImage = this.loader.load(this.$image.dataset.hover)this.sizes = new THREE.Vector2(0, 0)this.offset = new THREE.Vector2(0, 0)this.getSizes()this.createMesh()}
}

First, we create another class where we pass the scene as a property. We set two new vectors, dimension and offset, in which we’ll store the dimension and position of our DOM image.

首先,我们创建另一个类,将场景作为属性传递给该类。 我们设置了两个新的向量,尺寸和偏移,将在其中存储DOM图像的尺寸和位置。

Furthermore, we’ll use a TextureLoader to “load” our images and convert them into a texture. We need to do that as we want to use these pictures in our shaders.

此外,我们将使用TextureLoader来“加载”图像并将其转换为纹理。 我们需要这样做,因为我们想在着色器中使用这些图片。

We need to create a method in our class to handle the loading of our images and wait for a callback. We could achieve that with an async function but for this tutorial, let’s keep it simple. Just keep in mind that you’ll probably need to refactor this a bit for your own purposes.

我们需要在类中创建一个方法来处理图像的加载并等待回调。 我们可以使用异步功能来实现这一点,但是对于本教程而言,我们让它保持简单。 请记住,您可能需要出于自身目的对它进行一些重构。

// Figure.js// ...getSizes() {const { width, height, top, left } = this.$image.getBoundingClientRect()this.sizes.set(width, height)this.offset.set(left - window.innerWidth / 2 + width / 2, -top + window.innerHeight / 2 - height / 2)}
// ...

We get our image information in the getBoundingClientRect object. After that, we’ll pass these to our two variables. The offset is here to calculate the distance between the center of the screen and the object on the page.

我们在getBoundingClientRect对象中获取图像信息。 之后,我们将它们传递给我们的两个变量。 这里的偏移量用于计算屏幕中心与页面上的对象之间的距离。

// Figure.js// ...createMesh() {this.geometry = new THREE.PlaneBufferGeometry(1, 1, 1, 1)this.material = new THREE.MeshBasicMaterial({map: this.image})this.mesh = new THREE.Mesh(this.geometry, this.material)this.mesh.position.set(this.offset.x, this.offset.y, 0)this.mesh.scale.set(this.sizes.x, this.sizes.y, 1)this.scene.add(this.mesh)}
// ...

After that, we’ll set our values on the plane we’re building. As you can notice, we have created a plane of 1 on 1px with 1 row and 1 column. As we don’t want to distort the plane, we don’t need a lot of faces or vertices. So let’s keep it simple.

之后,我们将在正在构建的飞机上设置值。 如您所见,我们在1px上创建了一个平面,该平面上有1行1列。 因为我们不想使飞机变形,所以不需要很多面或顶点。 因此,让我们保持简单。

But why scale it while we can set the size directly? Glad you asked.

但是为什么要缩放它,而我们却可以直接设置大小呢? 很高兴你问。

Because of the resizing part. If we want to change the size of our mesh afterwards, there is no other proper way than this one. While it’s easier to change the scale of the mesh, it’s not for the dimension.

由于调整大小的一部分。 如果我们之后要更改网格的大小,则没有其他适当的方法。 虽然更容易更改网格的比例,但不适用于尺寸。

For the moment, we set a MeshBasicMaterial, just to see if everything is fine.

目前,我们设置了MeshBasicMaterial,只是看一切是否正常。

获取鼠标坐标 (Get mouse coordinates)

Now that we have built our scene with our mesh, we want to get our mouse coordinates and, to keep things easy, we’ll normalize them. Why normalize? Because of the coordinate system in shaders.

现在,我们已经使用网格构建了场景,我们想要获取鼠标坐标,并且为了使事情变得简单,我们将其标准化。 为什么要归一化? 由于着色器中的坐标系。

As you can see in the figure above, we have normalized the values for both of our shaders. So to keep things simple, we’ll prepare our mouse coordinate to match the vertex shader coordinate.

如上图所示,我们已经将两个着色器的值标准化了。 因此,为了简单起见,我们将准备鼠标坐标以匹配顶点着色器坐标。

If you’re lost at this point, I recommend you to read the Book of Shaders and the respective part of Three.js Fundamentals. Both have good advice and a lot of examples to help understand what’s going on.

如果您此时迷失了方向,建议您阅读《着色器手册》和Three.js基础知识的相应部分。 两者都有很好的建议,并提供了许多示例来帮助您了解正在发生的事情。

// Figure.js// ...this.mouse = new THREE.Vector2(0, 0)
window.addEventListener('mousemove', (ev) => { this.onMouseMove(ev) })// ...onMouseMove(event) {TweenMax.to(this.mouse, 0.5, {x: (event.clientX / window.innerWidth) * 2 - 1,y: -(event.clientY / window.innerHeight) * 2 + 1,})TweenMax.to(this.mesh.rotation, 0.5, {x: -this.mouse.y * 0.3,y: this.mouse.x * (Math.PI / 6)})
}

For the tween parts, I’m going to use TweenMax from GreenSock. This is the best library ever. EVER. And it’s perfect for our purpose. We don’t need to handle the transition between two states, TweenMax will do it for us. Each time we move our mouse, TweenMax will update the position and the rotation smoothly.

对于补间部分,我将使用GreenSock中的TweenMax 。 这是有史以来最好的图书馆。 永远这对于我们的目标而言是完美的。 我们不需要处理两个状态之间的转换,TweenMax会为我们完成。 每次移动鼠标,TweenMax都会平滑更新位置和旋转。

演示地址

One last thing before we continue: we’ll update our material from MeshBasicMaterial to ShaderMaterial and pass some values (uniforms), the device pixel ratio and shaders.

继续之前的最后一件事:我们将材质从MeshBasicMaterial更新为ShaderMaterial,并传递一些值(均匀值),设备像素比率和着色器。

// Figure.js// ...this.uniforms = {u_image: { type: 't', value: this.image },u_imagehover: { type: 't', value: this.hover },u_mouse: { value: this.mouse },u_time: { value: 0 },u_res: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }
}this.material = new THREE.ShaderMaterial({uniforms: this.uniforms,vertexShader: vertexShader,fragmentShader: fragmentShader,defines: {PR: window.devicePixelRatio.toFixed(1)}
})update() {this.uniforms.u_time.value += 0.01
}

We passed our two textures, the mouse position, the size of our screen and a variable called u_time which we will increment each frame.

我们传递了两个纹理,即鼠标位置,屏幕大小和一个名为u_time的变量,该变量将使每帧递增。

But keep in mind that it’s not the best way to do that. For example, we only need to increment when we are hovering the figure, not every frame. I’m not going into details, but performance-wise, it’s better to just update our shader only when we need it.

但是请记住,这不是最好的方法。 例如,当我们将鼠标悬停在图形上时,我们只需要增加,而不必在每一帧上增加。 我不讨论细节,而是考虑性能,最好仅在需要时更新着色器。

技巧背后的逻辑及如何使用噪音 (The logic behind the trick & how to use noise)

Still here? Nice! Time for some magic tricks.

还在? 真好! 是时候使用一些魔术了。

I will not explain what noise is and where it comes from. If you’re interested, be sure to read this page from The Book of Shaders. It’s well explained.

我不会解释什么是噪声以及噪声的来源。 如果您有兴趣,请务必阅读《 The Shader of Shaders》中的本页。 很好解释。

Long story short, Noise is a function that gives us a value between -1 and 1 based on values we pass through. It will output a random pattern but more organic.

长话短说,Noise是一个函数,它根据传递的值为我们提供介于-1和1之间的值。 它将输出随机模式,但更加有机。

Thanks to noise, we can generate a lot of different shapes, like maps, random patterns, etc.

多亏了噪音,我们才能生成许多不同的形状,例如地图,随机图案等。

Let’s start with a 2D noise result. Just by passing the coordinate of our texture, we’ll have something like a cloud texture.

让我们从2D噪声结果开始。 仅通过传递纹理的坐标,我们就可以得到类似云的纹理。

But there are several kinds of noise functions. Let’s use a 3D noise by giving one more parameter like … the time? The noise pattern will evolve and change over time. By changing the frequency and the amplitude, we can give some movement and increase the contrast.

但是,有几种噪声函数。 让我们使用3D噪声,例如再给一个参数,例如...时间? 噪声模式将随着时间的流逝而变化。 通过更改频率和幅度,我们可以进行一些移动并增加对比度。

It will be our first base.

这将是我们的第一基地。

Second, we’ll create a circle. It’s quite easy to build a simple shape like a circle in the fragment shader. We just take the function from The Book of Shaders: Shapes to create a blurred circle, increase the contrast and voilà!

其次,我们将创建一个圆。 在片段着色器中构建像圆形这样的简单形状非常容易。 我们只是采用了《 The Shader of Shaders :形状》中的功能来创建一个模糊的圆圈,增加对比度和外观!

Last, we add these two together, play with some variables, cut a “slice” of this and tadaaa:

最后,我们将这两个加在一起,使用一些变量,将其与tadaaa进行“切片”:

We finally mix our textures together based on this result and here we are, easy peasy lemon squeezy!

最后,根据此结果,我们将纹理混合在一起,在这里,轻松榨柠檬!

Let’s dive into the code.

让我们深入研究代码。

着色器 (Shaders)

We won’t really need the vertex shader here so this is our code:

我们在这里实际上并不需要顶点着色器,所以这是我们的代码:

// vertexShader.glsl
varying vec2 v_uv;void main() {v_uv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

ShaderMaterial from Three.js provides some useful default variables when you’re a beginner:

当您是初学者时,Three.js的ShaderMaterial提供了一些有用的默认变量:

  • position (vec3): the coordinates of each vertex of our mesh

    位置(vec3):网格的每个顶点的坐标

  • uv (vec2): the coordinates of our texture

    uv (vec2):纹理的坐标

  • normals (vec3): normal of each vertex our mesh have.

    法线(vec3):网格物体每个顶点的法线

Here we’re just passing the UV coordinates from the vertex shader to fragment shader.

在这里,我们只是将UV坐标从顶点着色器传递到片段着色器。

创建圈子 (Create the circle)

Let’s use the function from The Book of Shaders to build our circle and add a variable to handle the blurriness of our edges.

让我们使用“着色器书”中的函数来构建圆并添加一个变量来处理边缘的模糊性。

Moreover, we’ll add the mouse position to the origin of our circle. This way, the circle will be moving as long as we move our mouse over our image.

此外,我们将鼠标位置添加到圆的原点。 这样,只要我们将鼠标移到图像上,圆就会一直移动。

// fragmentShader.glsl
uniform vec2 u_mouse;
uniform vec2 u_res;float circle(in vec2 _st, in float _radius, in float blurriness){vec2 dist = _st;return 1.-smoothstep(_radius-(_radius*blurriness), _radius+(_radius*blurriness), dot(dist,dist)*4.0);
}void main() {// We manage the device ratio by passing PR constantvec2 res = u_res * PR;vec2 st = gl_FragCoord.xy / res.xy - vec2(0.5);// tip: use the following formula to keep the good ratio of your coordinatesst.y *= u_res.y / u_res.x;// We readjust the mouse coordinatesvec2 mouse = u_mouse * -0.5;// tip2: do the same for your mousemouse.y *= u_res.y / u_res.x;mouse *= -1.;vec2 circlePos = st + mouse;float c = circle(circlePos, .03, 2.);gl_FragColor = vec4(vec3(c), 1.);
}

演示地址

弄些东西(Make some noooooise)

As we saw above, the noise function has several parameters and gives us a smooth cloudy pattern. How could we have that? Glad you asked.

正如我们在上面看到的,噪声函数具有多个参数,并为我们提供了平滑的浑浊模式。 我们怎么有呢? 很高兴你问。

For this part, I’m using glslify and glsl-noise, and two npm packages to include other functions. It keeps our shader a little bit more readable and avoids having a lot of displayed functions that we will not use after all.

对于这一部分,我将使用glslify和glsl-noise以及两个npm软件包来包含其他功能。 它使我们的着色器更具可读性,并避免了很多我们根本不会使用的显示功能。

// fragmentShader.glsl
#pragma glslify: snoise2 = require('glsl-noise/simplex/2d')//...varying vec2 v_uv;uniform float u_time;void main() {// ...float n = snoise2(vec2(v_uv.x, v_uv.y));gl_FragColor = vec4(vec3(n), 1.);
}

By changing the amplitude and the frequency of our noise (exactly like the sin/cos functions), we can change the render.

通过更改噪声的幅度和频率(完全类似于sin / cos函数),我们可以更改渲染。

// fragmentShader.glslfloat offx = v_uv.x + sin(v_uv.y + u_time * .1);
float offy = v_uv.y - u_time * 0.1 - cos(u_time * .001) * .01;float n = snoise2(vec2(offx, offy) * 5.) * 1.;

But it isn’t evolving through time! It is distorted but that’s it. We want more. So we will use noise3d instead and pass a 3rd parameter: the time.

但这并没有随着时间的推移而发展! 它失真了,仅此而已。 我们想要更多。 因此,我们将改为使用noise3d并传递第三个参数:时间。

float n = snoise3(vec3(offx, offy, u_time * .1) * 4.) * .5;

As you can see, I changed the amplitude and the frequency to have the render I desire.

如您所见,我更改了幅度和频率以得到所需的渲染。

Alright, let’s add them together!

好吧,让我们将它们加在一起!

合并两个纹理 (Merging both textures)

By just adding these together, we’ll already see an interesting shape changing through time.

通过将它们加在一起,我们已经可以看到有趣的形状随时间变化。

To explain what’s happening, let’s imagine our noise is like a sea floating between -1 and 1. But our screen can’t display negative color or pixels more than 1 (pure white) so we are just seeing the values between 0 and 1.

为了解释正在发生的事情,让我们想象一下我们的噪音就像是在-1和1之间漂浮的大海。但是我们的屏幕无法显示负色或像素大于1(纯白色)的像素,因此我们只能看到0到1之间的值。

And our circle is like a flan.

我们的圈子就像果馅饼。

By adding these two shapes together it will give this very approximative result:

通过将这两个形状加在一起,将得到非常近似的结果:

Our very white pixels are only pixels outside the visible spectrum.

我们非常白的像素只是可见光谱之外的像素。

If we scale down our noise and subtract a small number, it will be completely moving down your waves until it disappears above the surface of the ocean of visible colors.

如果我们按比例缩小噪声并减去一小部分,它将完全沿您的波浪向下移动,直到它消失在水面之上。 海洋可见的颜色。

float n = snoise(vec3(offx, offy, u_time * .1) * 4.) - 1.;

Our circle is still there but not enough visible to be displayed. If we multiply its value, it will be more contrasted.

我们的圈子仍然存在,但可见度不足以显示。 如果我们乘以它的值,它将形成更大的对比。

float c = circle(circlePos, 0.3, 0.3) * 2.5;

We are almost there! But as you can see, there are still some details missing. And our edges aren’t sharp at all.

我们就快到了! 但是正如您所看到的,仍然缺少一些细节。 而且我们的边缘根本不锋利。

To avoid that, we’ll use the built-in smoothstep function.

为了避免这种情况,我们将使用内置的smoothstep函数。

float finalMask = smoothstep(0.4, 0.5, n + c);gl_FragColor = vec4(vec3(finalMask), 1.);

Thanks to this function, we’ll cut a slice of our pattern between 0.4 et 0.5, for example. The shorter the space is between these values, the sharper the edges are.

例如,借助此功能,我们将在0.4到0.5之间切出一部分图案。 这些值之间的间隔越短,边缘越锐利。

演示地址

Finally, we can mix our two textures to use them as a mask.

最后,我们可以混合两个纹理以将它们用作遮罩。

uniform sampler2D u_image;
uniform sampler2D u_imagehover;// ...vec4 image = texture2D(u_image, uv);
vec4 hover = texture2D(u_imagehover, uv);vec4 finalImage = mix(image, hover, finalMask);gl_FragColor = finalImage;

We can change a few variables to have a more gooey effect:

我们可以更改一些变量以产生更大的粘性:

// ...float c = circle(circlePos, 0.3, 2.) * 2.5;float n = snoise3(vec3(offx, offy, u_time * .1) * 8.) - 1.;float finalMask = smoothstep(0.4, 0.5, n + pow(c, 2.));// ...

And voilà!

和瞧!

演示地址

Check out the full source here or take a look at the live demo.

在此处查看完整的源代码或观看现场演示。

麦克风下降 (Mic drop)

Congratulations to those who came this far. I haven’t planned to explain this much. This isn’t perfect and I might have missed some details but I hope you’ve enjoyed this tutorial anyway. Don’t hesitate to play with variables, try other noise functions and try to implement other effects using the mouse direction or play with the scroll!

恭喜那些来到这里的人。 我没有计划对此做太多解释。 这并不完美,我可能已经错过了一些细节,但是我希望您仍然喜欢本教程。 不要犹豫,使用变量,尝试其他噪声函数,并尝试使用鼠标方向或滚动来实现其他效果!

If you have any questions, let me know in the comments section! I also encourage you to download the demo, it’s a little bit more complex and shows the effects in action with hover and click effects ¯\_(?)_/¯

如有任何疑问,请在评论部分让我知道! 我也鼓励您下载该演示,它稍微复杂一点,并通过悬停和单击效果来展示实际效果。\ _(?)_ /

参考和鸣谢 (References and Credits)

  • Images from Unsplash

    来自Unsplash的图像

  • Three.js

    Three.js

  • GSAP from GreenSock

    来自GreenSock的GSAP

  • Smooth Scrollbar

    平滑滚动条

  • glslify

    玻璃化

  • glsl-noise

    噪音

翻译自: https://tympanus.net/codrops/2019/10/23/making-gooey-image-hover-effects-with-three-js/

python gooey

python gooey_使用Three.js制作Gooey图像悬停效果相关推荐

  1. 纯JS制作的窗户雨滴效果

    今天本站推荐的代码是用JS制作的窗户雨滴效果,绚丽的效果不亚于FLASH,由于不知出处在哪,总而言之, 在此感谢作者的慷慨分享. function demo() { var engine = new ...

  2. 值得使用的CSS库添加图像悬停效果!

    一个悬停效果简单来说可以定义为在颜色,产生的效果文字的大小或形状或任何其他形式的变化发生,当你把鼠标光标移动到图像.这可以通过CSS编码容易实现.这里我们介绍国外8个CSS库添加图像悬停效果,提高网站 ...

  3. 教你用JavaScript制作背景图像滚动效果

    案例介绍 欢迎来到我的小院,我是霍大侠,恭喜你今天又要进步一点点了! 我们来用JavaScript编程实战案例,做一个背景图像滚动效果.滚动鼠标背景图像缩小,下方滑动出现文字.通过实战我们将学会obj ...

  4. 纯js制作图片轮播效果

    好久没有发博客了,最近都在复习,为了找工作做准备. 前段时间逛了下鼠绘漫画网追海贼王最新漫画,发现他们家的图片轮播效果跟其他网页的图片轮播效果效果不同,看起来更加有趣,于是我尝试了用js复写它. 前提 ...

  5. html导航栏悬停过渡,JS 实现导航栏悬停效果

    JS-实现导航栏悬停 先布个局: Test 1 tab1 tab2 tab3 tab4 2 3 4 5 6 7 添加简单的样式: div.main{ width: 800px; background: ...

  6. 十大酷炫屌的图像悬停特效

    本文阿宝哥将给大家介绍十个 "酷炫屌" 的图像悬停效果,希望阿宝哥精心录制的十个 Gif 动画能让大家眼前一亮,当然更希望这些特效能给大家设计图片悬停效果带来一些 "灵感 ...

  7. #3使用html+css+js制作网页 番外篇 使用python flask 框架 (I)

    #3使用html+css+js制作网页 番外篇 使用python flask 框架(I 第一部) 0. 本系列教程 1. 准备 a.python b. flask c. flask 环境安装 d. f ...

  8. python matpoltlib绘制动态图_使用Python、Geopandas和Matplotlib制作gif动态

    原标题:使用Python.Geopandas和Matplotlib制作gif动态 不需要Photoshop:仅使用Python和命令行制作动画图表. 作为一种编程语言,Python非常灵活.这使得有时 ...

  9. vue canvas插件_基于vue.js 制作在线桌椅定制选择交互特效源码

    码农那点事儿 关注我们,一起学习进步 基于vue.js写的在线桌子椅子垫子选择拼成的自己理想的书桌椅图像,这是一款交互式的课桌椅在线定制选择功能.非常不错,感兴趣的朋友前来下载使用. 下载源码(提取码 ...

最新文章

  1. python答案公众号_大学慕课用Python玩转数据答案查题公众号
  2. 大众mpv_一汽-大众全新MPV车型国内伪装路试曝光,没有侧滑门设计
  3. HDU - 5876 Sparse Graph(bfs+set)
  4. linux slub分配器浅析
  5. 微信红包封面向个人开放,1元定制!
  6. c语言程序设计实践教程编程题8.3,C语言程序设计教程(21世纪计算机科学与技术实践型教程)...
  7. jsp页面的相关复习
  8. TCP、UDP绑定同一端口通信的解释-转
  9. Codis安装部署全架构
  10. Xcode Developer Tools
  11. 利用mitmproxy进行抓包
  12. ES6 将数组根据某个属性进行分组的方法
  13. vue视频教程大全下载
  14. springboot接收前端的数组_SpringBoot如何接收数组参数的方法
  15. Aspose.Cells 使用UnMerge()取消合并单元格(取消合并单行,取消合并单列,取消多行多列)
  16. LocalDate 获取英文星期
  17. 爱搞事情:我的黑苹果日记之安装路
  18. 炫酷的网页特效展示分享
  19. 西安电子科技大学计算机834,西安电子科技大学834数据结构2021年硕士研究生招生考试自命题科目考试大纲...
  20. Activtiy完全解析(一、Activity的创建过程)

热门文章

  1. java嵌套循环例子:假定公鸡5元钱1只,母鸡3元钱1只,小鸡1元钱3只。现在有100元钱要求买100只鸡,请编程列出所有可能的购鸡方案。
  2. dfs bfs 完全没这概念啊 QAQ 咋这么抽象咩 快去找大佬学习哇哇哇~~
  3. Quaternion.Slerp
  4. 三级、四级嵌入式考察范围
  5. IBM openblockchain学习(一)--obc-peer环境搭建
  6. minio设置文件上传大小
  7. java facets_IDEA的社区版中Facets无法添加Web支持
  8. stm32滴答计时器_STM32嵌入式开发学习笔记(四):使用滴答计时器实现精准计时...
  9. 什么是前端,前端的现状以及未来发展趋势,最简单的理解前端开发
  10. 笔记-项目采购管理-竞争性谈判采购和单一来源采购