Показать сообщение отдельно
  #11 (permalink)  
Старый 22.02.2018, 11:24
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Рони, это ужасно!!! Но другой картинки не найти! Эта прямо на экране! Я тоже покручу, только на 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>
Ответить с цитированием