Пример:
Зажмите и держите левую кнопку мышки в любом месте страницы.
Я добавил фотографии разного жанра, чтобы эффект перехода был наиболее заметен. Но не забывайте, что все они должны быть одного размера!
HTML:
1 |
<canvas id="canvas"></canvas> |
CSS:
Если задать высоту для канваса, как в примере, то изображение будет умещаться в нем пропорциально, но обрезая края, которые не уместились.
Если не укахать высоту, то размер слайдера по высоте будет равен высоте изображения при ширине в 100%. Пропорции при этом сохранятся.
1 2 3 4 5 6 |
#canvas { width: 100%; object-fit: cover; height: 500px; margin: 20px 0; } |
JS:
Тестируя скрипт локально эффект может не работать.
Ссылки на изображения добавляются в массиве let images
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 |
let gl = canvas.getContext('webgl'); let silde = 0, canvasValue = 0, fisheyeValue = 0, fisheyeDirection = 0; let images = [ "/photo-1.jpg", "/photo-2.jpg", "/photo-3.jpg" ]; images.forEach((url, i) => loadTexture(i, url)); addEventListener('mousedown', () => fisheyeDirection = 0.02) addEventListener('mouseup', () => fisheyeDirection = -0.02) addEventListener('touchstart', () => fisheyeDirection = 0.02) addEventListener('touchend', () => fisheyeDirection = -0.02) let pid = gl.createProgram(); shader(` attribute vec2 c; void main(void) { gl_Position = vec4(c, 0.0, 1.0); } `, gl.VERTEX_SHADER); shader(` precision mediump float; uniform vec2 size; uniform float fisheye; uniform float val; uniform sampler2D tex[2]; void main(void) { vec2 uv = gl_FragCoord.xy / size.xy; vec2 c = uv - 0.5; float a = atan(c.y, c.x); uv = c * (1.0 - length(c)*fisheye/1.6) + 0.5; float d = sqrt(sqrt(dot(c, c))) + sin(a*7.)*0.02; float v = smoothstep(val - 0.1, val - 0.2, d); vec2 cs = vec2(cos(1.0 - val), sin(1.0 - val)); vec2 uv2 = (uv - 0.5)/sqrt(val)*mat2(cs, -cs.y, cs.x) + 0.5; gl_FragColor = mix(texture2D(tex[0], uv2), texture2D(tex[1], uv), 1.0 - v); } `, gl.FRAGMENT_SHADER); gl.linkProgram(pid); gl.useProgram(pid); gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,3,-1,-1,3,-1]), gl.STATIC_DRAW); let al = gl.getAttribLocation(pid, "c"); gl.vertexAttribPointer(al, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(al); let valueLoc = gl.getUniformLocation(pid, 'val'); let sizeLoc = gl.getUniformLocation(pid, 'size'); let fisheyeLoc = gl.getUniformLocation(pid, 'fisheye'); var texLoc = gl.getUniformLocation(pid, "tex"); requestAnimationFrame(draw) function nextSlide() { canvasValue = 1; gl.uniform1iv(texLoc, [ silde, silde = (silde + 1) % images.length ]); } function draw() { canvasValue = Math.max(0, canvasValue - 0.005); fisheyeValue = Math.max(0, Math.min(1, fisheyeValue + fisheyeDirection)) if (fisheyeValue > 0.7 && canvasValue < 0.2) nextSlide(); gl.uniform1f(valueLoc, canvasValue); gl.uniform1f(fisheyeLoc, fisheyeValue); gl.drawArrays(gl.TRIANGLES, 0, 3); requestAnimationFrame(draw) } function loadTexture(i, url) { let loader = new Image(); loader.crossOrigin = "anonymous"; loader.src = url; loader.onload = function() { let texture = gl.createTexture(); gl.activeTexture(gl.TEXTURE0 + i); gl.bindTexture(gl.TEXTURE_2D, texture); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true) gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, loader); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); if (i == 0) { canvas.width = canvas.offsetWidth; canvas.height = canvas.width * loader.height / loader.width; gl.uniform2f(sizeLoc, gl.drawingBufferWidth, gl.drawingBufferHeight) gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); } } } function shader(src, type) { let sid = gl.createShader(type); gl.shaderSource(sid, src); gl.compileShader(sid); let message = gl.getShaderInfoLog(sid); gl.attachShader(pid, sid); if (message.length > 0) { console.log(src.split("\n").map((str, i) => ("" + (1 + i)) .padStart(4, "0") + ": " + str).join("\n")); throw message; } } |
Найдено на codepen.io у пользователя Stranger in the Q и немного переделано
Добавить комментарий: