Шум реагирует на движение курсора мыши и на прокрутку страницы, а скрипт, который его реализует, не требует фреймворков и библиотек.
Пример:
Нейронный шум
Затемнение:
30%
Выбрать другой фон для примера:
HTML:
1 2 3 |
<div id="neuro"> <span data-title="Нейронный шум">Нейронный шум</span> </div> |
JS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
const shader = { vertex: `precision mediump float; varying vec2 vUv; attribute vec2 a_position; void main() { vUv = .5 * (a_position + 1.); gl_Position = vec4(a_position, 0.0, 1.0); }`, fragment: `precision mediump float; varying vec2 vUv; uniform float u_time; uniform float u_ratio; uniform vec2 u_pointer_position; uniform float u_scroll_progress; vec2 rotate(vec2 uv, float th) { return mat2(cos(th), sin(th), -sin(th), cos(th)) * uv; } float neuro_shape(vec2 uv, float t, float p) { vec2 sine_acc = vec2(0.); vec2 res = vec2(0.); float scale = 8.; for (int j = 0; j < 16; j++) { uv = rotate(uv, 1.); sine_acc = rotate(sine_acc, 1.); vec2 layer = uv * scale + float(j) + sine_acc - t; sine_acc += sin(layer); res += (.5 + .5 * cos(layer)) / scale; scale *= (1.2 - .07 * p); } return res.x + res.y; } void main() { vec2 uv = .5 * vUv; uv.x *= u_ratio; vec2 pointer = vUv - u_pointer_position; pointer.x *= u_ratio; float p = clamp(length(pointer), 0., 1.); p = .5 * pow(1. - p, 2.); float t = .001 * u_time; vec3 color = vec3(0.); float noise = neuro_shape(uv, t, p); noise = 1.2 * pow(noise, 3.); noise += pow(noise, 10.); noise = max(.0, noise - .5); noise *= (1. - length(vUv - .5)); color = normalize(vec3(.2, .5 + .4 * cos(3. * u_scroll_progress), .5 + .5 * sin(3. * u_scroll_progress))); color = color * noise; gl_FragColor = vec4(color, noise); }` }; const cont = document.getElementById('neuro'); const canvasEl = document.createElement("canvas"); cont.appendChild(canvasEl); const devicePixelRatio = Math.min(window.devicePixelRatio, 2); const pointer = { x: 0, y: 0, tX: 0, tY: 0, }; let uniforms; const gl = initShader(); setupEvents(); resizeCanvas(); window.addEventListener("resize", resizeCanvas); render(); function initShader() { const vsSource = shader.vertex; const fsSource = shader.fragment; const gl = canvasEl.getContext("webgl") || canvasEl.getContext("experimental-webgl"); if (!gl) { alert("WebGL is not supported by your browser."); } function createShader(gl, sourceCode, type) { const shader = gl.createShader(type); gl.shaderSource(shader, sourceCode); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { console.error("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; } const vertexShader = createShader(gl, vsSource, gl.VERTEX_SHADER); const fragmentShader = createShader(gl, fsSource, gl.FRAGMENT_SHADER); function createShaderProgram(gl, vertexShader, fragmentShader) { const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.error("Unable to initialize the shader program: " + gl.getProgramInfoLog(program)); return null; } return program; } const shaderProgram = createShaderProgram(gl, vertexShader, fragmentShader); uniforms = getUniforms(shaderProgram); function getUniforms(program) { let uniforms = []; let uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < uniformCount; i++) { let uniformName = gl.getActiveUniform(program, i).name; uniforms[uniformName] = gl.getUniformLocation(program, uniformName); } return uniforms; } const vertices = new Float32Array([-1., -1., 1., -1., -1., 1., 1., 1.]); const vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); gl.useProgram(shaderProgram); const positionLocation = gl.getAttribLocation(shaderProgram, "a_position"); gl.enableVertexAttribArray(positionLocation); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); return gl; } function render() { const currentTime = performance.now(); pointer.x += (pointer.tX - pointer.x) * .5; pointer.y += (pointer.tY - pointer.y) * .5; gl.uniform1f(uniforms.u_time, currentTime); gl.uniform2f(uniforms.u_pointer_position, pointer.x / cont.offsetWidth, 1 - pointer.y / cont.offsetHeight); gl.uniform1f(uniforms.u_scroll_progress, window["pageYOffset"] / (2 * cont.offsetHeight)); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); requestAnimationFrame(render); } function resizeCanvas() { canvasEl.width = cont.offsetWidth * devicePixelRatio; canvasEl.height = cont.offsetHeight * devicePixelRatio; gl.uniform1f(uniforms.u_ratio, canvasEl.width / canvasEl.height); gl.viewport(0, 0, canvasEl.width, canvasEl.height); } function setupEvents() { window.addEventListener("pointermove", e => { updateMousePosition(e.pageX, e.pageY); }); window.addEventListener("touchmove", e => { updateMousePosition(e.targetTouches[0].pageX, e.targetTouches[0].pageY); }); window.addEventListener("click", e => { updateMousePosition(e.pageX, e.pageY); }); function updateMousePosition(eX, eY) { pointer.tX = eX - cont.offsetLeft; pointer.tY = eY - cont.offsetTop; } } |
CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
#neuro { position: relative; height: 580px; margin: 20px 0; background-color: #000; background-image: url(photo.jpg); background-size: cover; background-position: center; display: flex; flex-flow: column; align-items: center; justify-content: center; text-align: center; overflow: hidden; z-index: 1; border-radius: 6px; box-shadow: 0 4px 8px rgba(0,0,0,0.15), 0 12px 18px rgba(0,0,0,0.1); } #neuro:after { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #000; opacity: 0.5; z-index: 2; } #neuro canvas { position: relative; z-index: 3; } #neuro span { color: #FFF; position: absolute; font-size: 50px; line-height: 1.3; font-weight: normal; font-family: Impact, Charcoal, sans-serif; text-shadow: 0 0 40px #000, 0 40px 100px #000, 0 -40px 100px #000, 150px 0 100px #000, -150px 0 100px #000, 0 0 4px rgba(0,0,0,0.2), 0 2px 6px rgba(0,0,0,0.1); text-transform: uppercase; letter-spacing: 2px; margin: 30px 0; display: block; z-index: 4; } #neuro span:after { content: attr(data-title); position: absolute; font-size: 100px; text-shadow: 0 40px 100px #000, 0 -40px 100px #000; top: 50%; left: 50%; width: 200%; transform: translateX(-50%) translateY(-50%); color: #FFF; opacity: 0.2; text-transform: uppercase; z-index: -1; } @media only screen and (max-width: 1199px) { #neuro span { font-size: 40px; } #neuro span:after { font-size: 80px; } } @media screen and (max-width:767px) { #neuro span { font-size: 30px; } #neuro span:after { font-size: 60px; } } |
- Фон секции задается в стиле
background-image
класса#neuro
- Затемнение задается в стиле
opacity
класса#neuro:after
За основу взято решение, найденное на codepen.io у пользователя Ksenia Kondrashova
Смотрите также:
Фон из точек с ховер эффектом на CSS
JavaScript, создающий фоновый эффект с появляющимися неоновыми полигонами
Эффект с появляющейся рандомной каплей в качестве фона для секции
Добавить комментарий: