Рони, это ужасно!!! Но другой картинки не найти! Эта прямо на экране! Я тоже покручу, только на CSS.
<div class="slider"></div>
<style>
.slider {
background: url(https://png.pngtree.com/element_origin_min_pic/16/11/24/4f7184d3cd5191b4cdbd63b9c3c4af25.jpg)
0px -50px / 600px 400px
no-repeat;
width: 138px;
height: 300px;
margin: 20px auto;
animation: hour-glass-rotation 1.5s infinite, hour-glass-frames 1.5s infinite steps(4);
}
@keyframes hour-glass-rotation {
from {
transform: rotate(180deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes hour-glass-frames {
from {
background-position: 0 -50px;
}
to {
background-position: -616px -50px;
}
}</style>
Кстати в Unicode есть символ "песочные часы": ⌛⏳⧖⧗
<div class="slider"></div>
<style>
.slider {
text-align: center;
}
.slider::before {
content: "\231B";
font-size: 150px;
animation: hour-glass 2s infinite;
display: inline-block;
}
@keyframes hour-glass {
from, to {
transform: rotate(0deg);
}
20%, 40% {
transform: rotate(-10deg) scale(1.1);
}
10%, 30% {
transform: rotate(10deg);
}
to {
transform: rotate(1turn) scale(1);
}
}
</style>
Правда тут нет песка...
Сообщение от j0hnik
|
Очень просто тут к сожалению не получится
|
Песок очень просто описывается при помощи блочного клеточного автомата. Если поделить пикселы на блоки 2×2 вертикально и горизонтально, а после каждого шага разделение на блоки сдвигать на один пиксель по горизонтали и вертикали, то все четыре пиксела любого блока оказываются в разных блоках на следующем шаге.
В пикселе может быть песок, а может и не быть. Так что в блоке размером 2×2 возможны 16 состоянии песка... На каждом шаге в этом блоке песок должен падать вниз. Можно ещё описать падение вверх (когда перевёрнуты часы)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
</head>
<body>
<style>
html {
background: linear-gradient(#FFFDE7, #E8EAF6);
height: 100vh;
}
body {
margin: 0;
height: 100vh;
display: flex;
overflow: hidden;
}
canvas.hour-glass {
image-rendering: pixelated;
margin: 1px;
display: inline-block;
transition: 500ms;
border: solid #444;
border-width: 5px 0;
margin: auto;
--transform-scale: scale(2);
transform: var(--transform-scale, ) var(--transform-rotate, );
border-radius: 5px;
}
</style>
<script>
class Hourglass {
constructor(width = 50, height = 75) {
this.width = width;
this.height = height;
this.context = document.createElement("canvas").getContext("2d");
document.addEventListener("click", this.clickHandler.bind(this));
let canvas = this.context.canvas;
canvas.classList.add("hour-glass");
document.body.appendChild(canvas);
canvas.width = width;
canvas.height = height;
canvas.style.display = "block";
this.step = 0; /* счётчик итерации */
this.direction = 0; /* перевёрнуты часы или нет? */
this.imageData = new ImageData(this.width, this.height);
this.init();
}
init() {
let data = this.imageData.data;
let isSand, isEmpty, x, y;
for (let i = 0, len = data.length; i < len; i += 4) {
x = i / 4 % this.width;
y = i / (4 * this.width) | 0;
/* где песок? */
isSand = y < .5 * this.height && y > .125 * this.height;
/* показывает, где не внутренности часов */
isEmpty = x < this.width * (.5 + .5 * Math.sin((y / this.height - .5) * Math.PI));
isEmpty ^= x > this.width * (.5 - .5 * Math.sin((y / this.height - .5) * Math.PI));
isEmpty &= !(x > this.width * .5 - 2 && x < this.width * .5 + 2);
/* покраска стекла, пустоты и песка */
data[i + 0] = isEmpty ? 0 : 255 * (.6 + Math.random() / 4);
data[i + 1] = isEmpty ? 0 : 255 * (.5 + Math.random() / 6);
data[i + 2] = isEmpty ? 0 : 255 * (.4 + Math.random() / 8);
data[i + 3] = isEmpty ? 0 : isSand ? 255 : 40;
}
this.context.putImageData(this.imageData, 0, 0);
this.render();
}
render() {
let data = this.imageData.data;
let a, b, c, d, key;
let ai, bi, ci, di;
for (let y = 1 + this.step % 2, h = this.height; y < h; y += 2) {
for (let x = this.step % 2, w = this.width; x < w; x += 2) {
/* если не внутренности часов, то идём дальше */
if (
data[ai = 4 * ((x + 0) + (y - 1) * w) + 3] == 0 ||
data[bi = 4 * ((x + 1) + (y - 1) * w) + 3] == 0 ||
data[ci = 4 * ((x + 0) + (y + 0) * w) + 3] == 0 ||
data[di = 4 * ((x + 1) + (y + 0) * w) + 3] == 0
) continue;
/* смотрим, где песчинки в окрестности 2х2 */
a = data[ai] != 40 ? 1 : 0;
b = data[bi] != 40 ? 1 : 0;
c = data[ci] != 40 ? 1 : 0;
d = data[di] != 40 ? 1 : 0;
/* находим их новые местоположения при помощи клеточного автомата */
key = ((((((a << 1) | b) << 1) | c) << 1) | d);
key = Hourglass.CA[key][this.direction];
data[ai] = (key & 8) == 8 ? 255 : 40;
data[bi] = (key & 4) == 4 ? 255 : 40;
data[ci] = (key & 2) == 2 ? 255 : 40;
data[di] = (key & 1) == 1 ? 255 : 40;
}
}
this.context.putImageData(this.imageData, 0, 0);
this.step++;
requestAnimationFrame(this.render.bind(this));
}
clickHandler() {
this.direction = (this.direction + 1) % 2;
this.context.canvas.style.setProperty("--transform-rotate", "rotate(" + this.direction / 2 + "turn)");
}
}
/*
правило клеточного автомата
имитирует падение песка
+---+---+
| A | B |
+---+---+
| C | D |
+---+---+
ABCD<i> -> [ABCD<i+1>, ABCD<i-1>]
*/
Hourglass.CA = {
0b0000: [0b0000, 0b0000],
0b0001: [0b0001, 0b0100],
0b0010: [0b0010, 0b1000],
0b0011: [0b0011, 0b0110],
0b0100: [0b0001, 0b0100],
0b0101: [0b0011, 0b1100],
0b0110: [0b0011, 0b1100],
0b0111: [0b0111, 0b1101],
0b1000: [0b0010, 0b1000],
0b1001: [0b0011, 0b1100],
0b1010: [0b0011, 0b1100],
0b1011: [0b1011, 0b1110],
0b1100: [0b0110, 0b1100],
0b1101: [0b0111, 0b1101],
0b1110: [0b1011, 0b1110],
0b1111: [0b1111, 0b1111]
};
document.addEventListener("DOMContentLoaded", event => {
new Hourglass();
});
</script>
</body>
</html>