Кто знает как можно реализовать такой круглый прогресс-бар с эффектом воды как в центральной части этого сайта https://asmobius.co.jp/
Подобные эффекты возможно сделать использовав фрагментный шейдер, в котором вычисляется каждый пиксель, в зависимости от положения слайдера.
Здесь наблюдается 2 слоя:
Один - 2 картинки одна поверх другой, между которыми происходит переход при помощи прозрачности картинки и сдвиг в противоположные стороны.
Второй - кольцо, внутри которого так же находятся обе картинки, с границей, определяемой значением слайдера + искажение текстурных координат, для имитации эффекта воды.
Для реализации я взял и скретил свои же старые ответы: эффект воды и огненное кольцо
let pid, timeLocarion, valueLocation,
v = Math.PI, value = Math.PI, gl = canvas.getContext('webgl');
loadTexture(0, "https://picsum.photos/id/88/400/400")
loadTexture(1, "https://picsum.photos/id/77/400/400")
addEventListener('wheel', s => {
value = value - Math.sign(s.deltaY)*0.1;
})
pid = gl.createProgram();
shader(`attribute vec2 c;void main(void){gl_Position=vec4(c,0.,1.);}`,gl.VERTEX_SHADER);
shader(`
precision lowp float;
uniform vec2 size; // размер стороны квадрата этого беспредела
uniform float time; // время
uniform float val; // значение слайдера, от минус пи до пи
uniform sampler2D tex1; // первая текстура
uniform sampler2D tex2; // вторая ткестура
// эффект воды
vec2 waterEffect(vec2 uv) {
float s = 0.;
vec2 p = uv;
for (int i = 0; i < 5; i++) {
vec2 adjc = p;
float theta = 2. * 3.1415 / 11.*float(i);
adjc.x += cos(theta)*time*.1 + time * .03;
adjc.y -= sin(theta)*time*.1 - time * .07;
float a = adjc.x*cos(theta) - adjc.y*sin(theta);
s += cos(a*15.) * 3.1415;
}
return p += sin(s)*.01;
}
void main(void) {
// текстурные координаты
vec2 uv = gl_FragCoord.xy / size.xy;
// сдвигаем текстурные координаты, чтобы отсчет начинался от центра экрана
vec2 c = uv - .5;
// делаем из прямоугольника квадрат, чтобы был круг а не эллипс
c.x *= size.x/size.y;
// если фрагмент вне кольца
if (abs(sqrt(dot(c, c)) - .4) > .05) {
// смешиваем 2 текстуры в пропорциях в зависимости от положения слайдера
float t = val/6.28 + .5;
gl_FragColor = texture2D(tex1, vec2(uv.x + t*.1, uv.y))*t
+ texture2D(tex2, vec2(uv.x - t*.1, uv.y))*(1.-t);
} else { // само кольцо
vec2 water = waterEffect(uv);
// тут небольшой хак с арктангенсом, обратите внимание на порядок следования
/// аргументов, у нормального арктангенса первым аргументом идет y а вторым x,
// перемена мест аргументов меняет местами оси системы координат - теперь
//граница на кольце не слева а сверху
if (atan(c.x, -c.y) > val) //справа или слева от границы ?
gl_FragColor = texture2D(tex1, water);
else
gl_FragColor = texture2D(tex2, water);
}
}
`, 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);
timeLocation = gl.getUniformLocation(pid, 'time');
valueLocation = gl.getUniformLocation(pid, 'val');
let sizeLocation = gl.getUniformLocation(pid, 'size');
gl.uniform2f(sizeLocation, gl.drawingBufferWidth, gl.drawingBufferHeight)
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
requestAnimationFrame(draw)
function loadTexture(i, url) {
let loader = new Image();
loader.crossOrigin = "anonymous";
loader.src = url;
loader.onload = function() {
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"+i), i);
gl.activeTexture(gl.TEXTURE1);
}
}
function draw(t) {
let speed = Math.max(0.0001, Math.abs(value-v)*0.1);
if (v < value) v = Math.min(value, v+speed);
if (v > value) v = Math.max(value, v-speed);
let val = v;
while (val < -Math.PI) val += Math.PI*2;
while (val > Math.PI) val -= Math.PI*2;
gl.clearColor(0, 0, 0, 0);
gl.uniform1f(timeLocation, t / 1000);
gl.uniform1f(valueLocation, val);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame(draw)
}
function shader(src, type) {
let sid = gl.createShader(type);
gl.shaderSource(sid, src);
gl.compileShader(sid);
var 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;
}
}
<canvas id="canvas" width=400 height=400></canvas>
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Как сделать так что бы отображался только один день недели, и по нажатию менялся на другой? код
Функция объявленная как "function declaration" создаётся еще до выполнения кода, на стадии инициализацииЭто относится и к функциям в глобальном объекте...
Нужно сложить значения неизвестного кол-ва inputСумму вывести в другой input