#javascript #html #css #svg #изображения
Подскажите, пожалуйста, есть ли возможность для фонового изображения создать эффект
рыбьего глаза fish eye для фонового изображения?
Пример изображения :
body {
position: relative;
background-color: #d0f0f9;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'
width='80' height='80' viewBox='0 0 80 80'%3E%3Cg fill='%231564e3' fill-opacity='0.4'%3E%3Cpath
fill-rule='evenodd' d='M0 0h40v40H0V0zm40 40h40v40H40V40zm0-40h2l-2 2V0zm0 4l4-4h2l-6
6V4zm0 4l8-8h2L40 10V8zm0 4L52 0h2L40 14v-2zm0 4L56 0h2L40 18v-2zm0 4L60 0h2L40 22v-2zm0
4L64 0h2L40 26v-2zm0 4L68 0h2L40 30v-2zm0 4L72 0h2L40 34v-2zm0 4L76 0h2L40 38v-2zm0
4L80 0v2L42 40h-2zm4 0L80 4v2L46 40h-2zm4 0L80 8v2L50 40h-2zm4 0l28-28v2L54 40h-2zm4
0l24-24v2L58 40h-2zm4 0l20-20v2L62 40h-2zm4 0l16-16v2L66 40h-2zm4 0l12-12v2L70 40h-2zm4
0l8-8v2l-6 6h-2zm4 0l4-4v2l-2 2h-2z'/%3E%3C/g%3E%3C/svg%3E");
margin: 0;
padding: 0;
max-height: 100vh;
overflow: hidden;
}
Ответы
Ответ 1
Именно так как вы хотите создать эффект рыбьего глаза для фона будет слишком трудоёмкой задачей, но можно создать полотно (canvas), в которое можно загрузить любую картинку, и затем это полотно расположить сверху вместо фона. Пример var bg = document.getElementById('bg'), c = document.getElementById('fish-eye'), ctx = c.getContext('2d'), bgCtx = bg.getContext('2d'), src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80' viewBox='0 0 80 80'%3E%3Cg fill='%231564e3' fill-opacity='0.4'%3E%3Cpath fill-rule='evenodd' d='M0 0h40v40H0V0zm40 40h40v40H40V40zm0-40h2l-2 2V0zm0 4l4-4h2l-6 6V4zm0 4l8-8h2L40 10V8zm0 4L52 0h2L40 14v-2zm0 4L56 0h2L40 18v-2zm0 4L60 0h2L40 22v-2zm0 4L64 0h2L40 26v-2zm0 4L68 0h2L40 30v-2zm0 4L72 0h2L40 34v-2zm0 4L76 0h2L40 38v-2zm0 4L80 0v2L42 40h-2zm4 0L80 4v2L46 40h-2zm4 0L80 8v2L50 40h-2zm4 0l28-28v2L54 40h-2zm4 0l24-24v2L58 40h-2zm4 0l20-20v2L62 40h-2zm4 0l16-16v2L66 40h-2zm4 0l12-12v2L70 40h-2zm4 0l8-8v2l-6 6h-2zm4 0l4-4v2l-2 2h-2z'/%3E%3C/g%3E%3C/svg%3E"; function loadImg(src) { var img = new Image(); img.crossOrigin = 'Anonymous'; img.onload = function() { bgCtx.fillStyle = '#d0f0f9'; bgCtx.fillRect(0, 0, bg.width, bg.height); for(var x = 0; x < bg.width; x += img.width) for(var y = 0; y < bg.height; y += img.height) bgCtx.drawImage(img, x, y, img.width, img.height); window.removeEventListener('mousemove', distortion); window.removeEventListener('touchmove', distortion); window.addEventListener('mousemove', distortion); window.addEventListener('touchmove', distortion); } img.src = src; } loadImg(src); document.getElementById('file').onchange = function(e) { var tgt = e.target || e.srcElement; loadImg(URL.createObjectURL(tgt.files[0])); }; function distortion(e) { var cx = (e.touches ? e.touches[0].clientX : e.clientX), cy = (e.touches ? e.touches[0].clientY : e.clientY), size = 200, zoom = 1; c.width = c.height = size; c.style.left = cx - size / 2 + 'px'; c.style.top = cy - size / 2 + 'px'; ctx.fillStyle = '#000'; ctx.fillRect(0, 0, size, size); ctx.drawImage( bg, cx - bg.offsetLeft - .5 * size / zoom, cy - bg.offsetTop - .5 * size / zoom, size / zoom, size / zoom, 0, 0, size, size ); var imgData = ctx.getImageData(0, 0, size, size), pixels = imgData.data, pixelsCopy = [], index = 0, h = size, w = size, i = 0; for(; i <= pixels.length; i+=4) { pixelsCopy[index] = [pixels[i], pixels[i+1], pixels[i+2], pixels[i+3]]; index++; } var result = fisheye(pixelsCopy, w, h); for(i = 0; i < result.length; i++) { index = 4 * i; if(result[i] != undefined) { pixels[index + 0] = result[i][0]; pixels[index + 1] = result[i][1]; pixels[index + 2] = result[i][2]; pixels[index + 3] = result[i][3]; } } ctx.putImageData(imgData, 0, 0); } function fisheye(srcpixels, w, h) { var dstpixels = srcpixels.slice(), y = 0; for(; y < h; y++) { var ny = ((2 * y) / h) - 1, ny2 = ny * ny, x = 0; for(; x < w; x++) { var nx = ((2 * x) / w) - 1, nx2 = nx * nx, r = Math.sqrt(nx2 + ny2); if(0.0 <= r && r <= 1.0) { var nr = Math.sqrt(1.0-r*r); nr = (r + (1.0-nr)) / 2.0; if(nr <= 1.0) { var theta = Math.atan2(ny, nx), nxn = nr * Math.cos(theta), nyn = nr * Math.sin(theta), x2 = parseInt(((nxn + 1) * w) / 2), y2 = parseInt(((nyn + 1) * h) / 2), srcpos = parseInt(y2 * w + x2); if(srcpos >= 0 & srcpos < w * h) dstpixels[parseInt(y * w + x)] = srcpixels[srcpos] } } } } return dstpixels } body{overflow: hidden; background: #d0f0f9} canvas,#button{position: absolute} #c{border-radius: 50%; pointer-events: none}
Вот ссылка на хорошую картинку для загрузки в этот пример (её надо сначала сохранить как файл). Код из этого примера был найден здесь, но изначально был взят отсюда. См. также: https://ru.wikipedia.org/wiki/ДисторсияОтвет 2
Этот эффект я бы назвал magnifier (эффект увеличительного стекла). Такой не супер сложно реализовать а WebGL/GLSL во фрагментном шейдере. Если убрать весь инфраструктурный код останется этот кусочек кода: vec2 c = uv - m; // текстурные координаты минус координаты мыши float r = 0.05; // радиус эффекта float a = dot(c, c); // дистанция от фрагмента до мышки float f = a > r ? a : a + a/2.0*(a/r - 1.0); // сила эффекта gl_FragColor = texture2D(tex, m + c*f/a); let url = "https://picsum.photos/id/28/650/650"; let gl = canvas.getContext('webgl'); let loader = new Image(); loader.crossOrigin = "anonymous"; loader.src = url; loader.onload = function() { let z = 1; let w = canvas.width = loader.width; let h = canvas.height = loader.height; pid = gl.createProgram(); shader(` attribute vec2 coords; varying vec2 uv; void main(void) { uv = coords.xy*0.5 + 0.5; gl_Position = vec4(coords.xy, 0.0, 1.0); } `, gl.VERTEX_SHADER); shader(` precision highp float; uniform sampler2D tex; varying vec2 uv; uniform vec3 m; void main(void) { vec2 c = uv - m.xy; float r = 0.05*m.z; float a = dot(c, c); float f = a > r ? a : a + a/2.0*(a/r - 1.0); gl_FragColor = texture2D(tex, m.xy + c*f/a); } `, 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, "coords"); gl.vertexAttribPointer(al, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(al); let texture = gl.createTexture(); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true) gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, loader); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 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); gl.uniform1i(gl.getUniformLocation(pid, "tex"), 0); let mouseLocation = gl.getUniformLocation(pid, 'm'); gl.viewport(0, 0, w, h); gl.clearColor(0, 0, 0, 0); draw(0, 0); canvas.addEventListener('mousemove', e => draw(e.layerX, e.layerY)); canvas.addEventListener('mousewheel', e => draw(e.layerX, e.layerY, z *= 1 - Math.sign(e.wheelDeltaY)*0.1)); function draw(x, y) { gl.uniform3f(mouseLocation, x/w, 1 - y/h, z); gl.drawArrays(gl.TRIANGLES, 0, 3); } } function shader(src, type) { let sid = gl.createShader(type); gl.shaderSource(sid, src); gl.compileShader(sid); gl.attachShader(pid, sid); } PS: радиус теперь можно регулировать колесом мыши PPS: эффект "рыбьего глаза", как и возможность его убрать - реализован тут, так же через WebGL/GLSL
Комментариев нет:
Отправить комментарий