предисловие
Случайно соприкоснулся с 3D-свойствами CSS, и родился своего рода дизайн.3D игрыидея.
Все учащиеся, изучавшие свойства css3D, должны знатьperspective
,perspective-origin
,transform-style: preserve-3d
Три значения атрибута,
Они составляют трехмерный мир CSS.
В то же время существуютtransform
свойства для перемещения, масштабирования, поворота и растяжения 3D-узлов.
Значение атрибута очень простое и редко используется в нашей обычной веб-разработке.
Можете ли вы создавать 3D-игры с этими свойствами CSS3D?
Конечно, это возможно.
Даже с единственной песочницей естьмой МирТакой шедевр.
Сегодня я познакомлю вас с совершенно новым 3D-приключением, которого вы никогда раньше не видели.
Без лишних слов давайте посмотрим на эффект:
Вот пробный адресИграть на ПК
мы собираемся закончить этолабиринт битва, необходимо выполнить следующие шаги:
- Создайте трехмерный мир
- Написать функцию для 3D-камеры
- Создайте трехмерный лабиринт
- Создайте игрока, который может свободно двигаться
- Найти подсказку кратчайшего пути в лабиринте
Сначала рассмотрим некоторые предварительные условия.
Знания и концепции, необходимые для создания CSS3D-игры
Система координат CSS3D
В css3D мы должны сначала прояснить концепцию,3DСистема координат.
использоватьлевая система координат, вытяните левую руку, большой и указательный пальцыLОстальные пальцы располагаются перпендикулярно указательному пальцу, как показано на рисунке:
Большой палец — это ось X, указательный палец — ось Y, а остальные пальцы — ось Z.
Это система координат в CSS3D.
Перспективные свойства
perspective
Для свойства перспективы в css.
Что означает этот атрибут Мы можем рассматривать наши глаза как точку наблюдения, а расстояние от глаз до целевого объекта — это расстояние просмотра, которое является упомянутым здесь атрибутом перспективы.
все знают,"Перспектива" + "2D" = "3D".
perspective: 1200px;
-webkit-perspective: 1200px;
3D камера
В разработке 3D-игр будет концепция камеры, то есть то, что видит человеческий глаз, это то, что видит камера.
В движении сцены в игре большая часть приходится на перемещение камеры.
Например, в гоночной игре камера движется вместе с автомобилем, поэтому по пути мы можем видеть пейзажи.
Здесь мы будем использовать CSS для реализации псевдо 3D-камеры.
Преобразование свойств
В CSS3D мы переводим, поворачиваем, растягиваем и масштабируем 3D-блок, используяtransform
Атрибуты.
- translateX переводит ось X
- translateY переводит ось Y
- translateZ переводит ось Z
- rotateX вращать ось X
- rotateY вращает ось Y
- rotateZ вращает ось Z
- rotate3d(x,y,z,deg) на сколько градусов повернуть оси X, Y, Z
Уведомление:
Есть разница между "перевести, а затем повернуть" и "повернуть, а затем перевести"
Угол поворота – это значение угла.
Здесь все еще есть студенты, которым непонятно, вы можете обратиться к этой статье Ю Фэя [Наггетс.Талант/пост/699769…] Поставляется с демонстрацией
матричное преобразование
Матричные преобразования используются в процессе прохождения игры.
В js получить узелtransform
атрибут, у вас получится матрица, вот я ее распечатаю, выглядит это так:
var _ground = document.getElementsByClassName("ground")[0];
var bg_style = document.defaultView.getComputedStyle(_ground, null).transform;
console.log("矩阵变换---->>>",bg_style)
Итак, как мы используем матрицу для управления преобразованием?
В линейных преобразованиях мы все используем умножение матриц.
CSS3D использует матрицу 4*4 для 3D-преобразования.
Следующие матрицы представлены двумерными массивами.
Напримерmatrix3d(1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1)
Он может быть представлен как двумерный массив:
[
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
]
Для перевода, даже если исходную матрицу состояния умножить на следующую матрицу, dx, dy, dz — направления движения x, y, z соответственно.
[
[1, 0, 0, dx],
[0, 1, 0, dy],
[0, 0, 1, dz],
[0, 0, 0, 1]
]
Поверните 𝞱 вокруг оси X, то есть умножьте на следующую матрицу.
[
[1, 0, 0, 0],
[0, cos𝞱, sin𝞱, 0],
[0, -sin𝞱, cos𝞱, 0],
[0, 0, 0, 1]
]
Поверните 𝞱 вокруг оси Y, то есть умножьте на следующую матрицу.
[
[cos𝞱, 0, -sin𝞱, 0],
[0, 1, 0, 0],
[sin𝞱, 0, cos𝞱, 0],
[0, 0, 0, 1]
]
Поверните 𝞱 вокруг оси Z, то есть умножьте на следующую матрицу.
[
[cos𝞱, sin𝞱, 0, 0],
[-sin𝞱, cos𝞱, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
]
Другие знания конкретной матрицы обсуждаются здесь, и все желающие могут изучить их самостоятельно.
Нам нужно только очень простое приложение вращения здесь.
Начните создавать трехмерный мир
Давайте сначала создадим интерфейс пользовательского интерфейса.
- камера див
- горизонт дел
- шахматная доска div
- Плеер div (здесь куб)
Уведомление
Куб сначала вращается, а затем перемещается, что должно быть самым простым методом.
Плоскость поворачивается на 180 градусов и ±90 градусов вокруг оси X и оси Y, и ей нужно только сместить ось Z.
Здесь все поймут.
Давайте сначала посмотрим на часть html:
<div class="camera">
<!-- 地面 -->
<div class="ground">
<div class="box">
<div class="box-con">
<div class="wall">z</div>
<div class="wall">z</div>
<div class="wall">y</div>
<div class="wall">y</div>
<div class="wall">x</div>
<div class="wall">x</div>
<div class="linex"></div>
<div class="liney"></div>
<div class="linez"></div>
</div>
<!-- 棋盘 -->
<div class="pan"></div>
</div>
</div>
</div>
очень простая планировка, гдеlinex
,liney
,linez
Это вспомогательная линия оси координат, которую я провел.
Красная линия — ось X, зеленая линия — ось Y, а синяя линия — ось Z.
Далее давайте взглянем на основной код CSS куба.
...
.box-con{
width: 50px;
height: 50px;
transform-style: preserve-3d;
transform-origin: 50% 50%;
transform: translateZ(25px) ;
transition: all 2s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.wall{
width: 100%;
height: 100%;
border: 1px solid #fdd894;
background-color: #fb7922;
}
.wall:nth-child(1) {
transform: translateZ(25px);
}
.wall:nth-child(2) {
transform: rotateX(180deg) translateZ(25px);
}
.wall:nth-child(3) {
transform: rotateX(90deg) translateZ(25px);
}
.wall:nth-child(4) {
transform: rotateX(-90deg) translateZ(25px);
}
.wall:nth-child(5) {
transform: rotateY(90deg) translateZ(25px);
}
.wall:nth-child(6) {
transform: rotateY(-90deg) translateZ(25px);
}
Кажется глупым вставлять кучу кода CSS.
Другой CSS не будет вставлен сюда, заинтересованные студенты могут загрузить его напрямую.исходный кодПроверять.
Построение интерфейса завершено, как показано на рисунке:
Дальше основное событие, идём писать js код, чтобы продолжить доделывать нашу игру.
Выполните функцию 3D-камеры
Камеры незаменимы в 3D-разработке.Использование функции камеры позволяет не только просматривать 3D-модель мира, но и реализовывать множество интересных функций в реальном времени.
Какие функции нужны 3d камере?
Самый простой, вверх-вниз, влево-вправо может обозревать карту на 360 градусов без тупиков, при этом нужно увеличивать и уменьшать масштаб.
Взаимодействие с мышью
Двигайте мышью влево и вправо, чтобы вращать для просмотра карты; Двигайте мышь вверх и вниз, чтобы наблюдать за картой вверх и вниз; Колесо мыши может увеличивать и уменьшать масштаб.
✅1. Слушайте события мыши
Прежде всего, нам нужно записать положение мыши, прослушивая события мыши, чтобы судить о том, что камера смотрит вверх, вниз, влево и вправо.
/** 鼠标上次位置 */
var lastX = 0, lastY = 0;
/** 控制一次滑动 */
var isDown = false;
/** 监听鼠标按下 */
document.addEventListener("mousedown", (e) => {
lastX = e.clientX;
lastY = e.clientY;
isDown = true;
});
/** 监听鼠标移动 */
document.addEventListener("mousemove", (e) => {
if (!isDown) return;
let _offsetX = e.clientX - lastX;
let _offsetY = e.clientY - lastY;
lastX = e.clientX;
lastY = e.clientY;
//判断方向
var dirH = 1, dirV = 1;
if (_offsetX < 0) {
dirH = -1;
}
if (_offsetY > 0) {
dirV = -1;
}
});
document.addEventListener("mouseup", (e) => {
isDown = false;
});
✅2. Определите камеру вверх, вниз, влево и вправо
использоватьperspective-origin
для установки линии обзора камеры вверх и вниз.
использоватьtransform
Повернуть ось Z для просмотра на 360 градусов влево и вправо.
/** 监听鼠标移动 */
document.addEventListener("mousemove", (e) => {
if (!isDown) return;
let _offsetX = e.clientX - lastX;
let _offsetY = e.clientY - lastY;
lastX = e.clientX;
lastY = e.clientY;
var bg_style = document.defaultView.getComputedStyle(_ground, null).transform;
var camera_style = document.defaultView.getComputedStyle(_camera, null).perspectiveOrigin;
var matrix4 = new Matrix4();
var _cy = +camera_style.split(' ')[1].split('px')[0];
var str = bg_style.split("matrix3d(")[1].split(")")[0].split(",");
var oldMartrix4 = str.map((item) => +item);
var dirH = 1, dirV = 1;
if (_offsetX < 0) {
dirH = -1;
}
if (_offsetY > 0) {
dirV = -1;
}
//每次移动旋转角度
var angleZ = 2 * dirH;
var newMartri4 = matrix4.set(Math.cos(angleZ * Math.PI / 180), -Math.sin(angleZ * Math.PI / 180), 0, 0, Math.sin(angleZ * Math.PI / 180), Math.cos(angleZ * Math.PI / 180), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
var new_mar = null;
if (Math.abs(_offsetX) > Math.abs(_offsetY)) {
new_mar = matrix4.multiplyMatrices(oldMartrix4, newMartri4);
} else {
_camera.style.perspectiveOrigin = `500px ${_cy + 10 * dirV}px`;
}
new_mar && (_ground.style.transform = `matrix3d(${new_mar.join(',')})`);
});
Здесь используется матричный метод для поворота оси Z, матричный классMatrix4
Это класс методов, который я написал временно, всего два метода, один задает двумерный массивmatrix4.set
, умножение матрицmatrix4.multiplyMatrices
.
В конце статьи есть адреса исходного кода, поэтому я не буду их здесь повторять.
✅3. Колесо монитора увеличивает и уменьшает масштаб
Здесь основано наperspective
для установки расстояния просмотра.
//监听滚轮
document.addEventListener('mousewheel', (e) => {
var per = document.defaultView.getComputedStyle(_camera, null).perspective;
let newper = (+per.split("px")[0] + Math.floor(e.deltaY / 10)) + "px";
_camera.style.perspective = newper
}, false);
Уведомление:
Свойство "perspective-origin" имеет только два значения X и Y, и это не может быть та же камера, что и u3D.
Здесь я использовал хитрое вращение горизонта, чтобы добиться того же эффекта.
Колесо прокрутки немного неудобно для увеличения и уменьшения масштаба, и оно все еще сильно отличается от 3D-движка.
После завершения вы можете увидеть следующую сцену, и вы можете наблюдать за нашей картой в любое время.
Таким образом, 3D-камера завершена.Если интересно, можете спуститься и написать ее сами, все равно очень интересно.
рисование доски лабиринта
Нарисовать карту-сетку проще всего, здесь я использую массив 15*15.
"0” представляет собой дорогу, по которой можно пройти, “1” представляет собой препятствие.
var grid = [
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0,
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0,
1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0,
0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1,
0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0
];
Затем мы проходим по этому массиву и получаем карту.
Напишите метод для создания сетки карты, возвращающий как массив сетки, так и массив узлов.
здесьblock
это сборка, созданная в html, это куб.
Затем добавьте его на шахматную доску, клонировав узел.
/** 棋盘 */
function pan() {
const con = document.getElementsByClassName("pan")[0];
const block = document.getElementsByClassName("block")[0];
let elArr = [];
grid.forEach((item, index) => {
let r = Math.floor(index / 15);
let c = index % 15;
const gezi = document.createElement("div");
gezi.classList = "pan-item"
// gezi.innerHTML = `${r},${c}`
con.appendChild(gezi);
var newBlock = block.cloneNode(true);
//障碍物
if (item == 1) {
gezi.appendChild(newBlock);
blockArr.push(c + "-" + r);
}
elArr.push(gezi);
});
const panArr = arrTrans(15, grid);
return { elArr, panArr };
}
const panData = pan();
Как видите, наш интерфейс стал таким.
Далее нам нужно управлять движением игрока.
управлять движением игрока
через вверх и внизw s a d
клавиши для управления движением игрока.
использоватьtransform
для перемещения и поворота коробки игрока.
✅Слушайте события клавиатуры
Прослушивая события клавиатурыonkeydown
судитьkey
значение вверх и вниз.
document.onkeydown = function (e) {
/** 移动物体 */
move(e.key);
}
✅сделать перемещение
В смещении используйтеtranslate
Для перемещения ось Z всегда обращена к нашей камере, поэтому нам нужно перемещать только оси X и Y.
Объявите переменную для записи текущей позиции.
При этом необходимо зафиксировать последнее преобразованиеtransform
При значении , здесь мы не продолжаем преобразование матрицы.
/** 当前位置 */
var position = { x: 0, y: 0 };
/** 记录上次变换值 */
var lastTransform = {
translateX: '0px',
translateY: '0px',
translateZ: '25px',
rotateX: '0deg',
rotateY: '0deg',
rotateZ: '0deg'
};
Каждую сетку можно рассматривать как нижний индекс двумерного массива, и каждый раз мы перемещаем сетку на расстояние.
switch (key) {
case 'w':
position.y++;
lastTransform.translateY = position.y * 50 + 'px';
break;
case 's':
position.y--;
lastTransform.translateY = position.y * 50 + 'px';
break;
case 'a':
position.x++;
lastTransform.translateX = position.x * 50 + 'px';
break;
case 'd':
position.x--;
lastTransform.translateX = position.x * 50 + 'px';
break;
}
//赋值样式
for (let item in lastTransform) {
strTransfrom += item + '(' + lastTransform[item] + ') ';
}
target.style.transform = strTransfrom;
На этом этапе наш ящик игрока можно перемещать.
Уведомление
Перевод в css3D можно увидеть как мировые координаты.
Так что нам нужно заботиться только об осях X и Y. И нам не нужно перемещать ось Z. Даже если мы вращаемся.
✅Вращение во время движения
В CSS3D 3D вращение отличается от других 3D движков.В общем,таких как u3D и threejs,после каждого вращения оно будет перекалибровано в мировые координаты,условно говоря
Легко подсчитать, на сколько градусов нужно повернуть вокруг какой оси.
Однако я также недооценил вращение CSS3D.
Я думал, что будет легко прокручивать куб вверх, вниз, влево и вправо, но это не так.
Вращение в CSS3D включает в себя кватернионы и карданные блокировки.
Например, мы вращаем поле нашего игрока. как показано на рисунке:
Во-первых, первая сетка (0,0) поворачивается на 90 градусов вверх и вокруг оси X, чтобы достичь (1,0); если ее повернуть на 90 градусов влево вокруг оси Y, она может достичь (0,1); Тогда мы можем получить правило следующим образом:
Как показано на рисунке, нет проблем в простом вращении вверх-вниз, влево-вправо вокруг оси, но чтобы повернуться к красной сетке, есть два разных способа перемещения.После достижения красной сетки будет два возможности ротации. Это приводит к ошибке вращения.
В то же время, хотя это правило трудно найти, его можно записать.Неправильно поворачивать блок в CSS3D в соответствии с этим правилом.
Потом кто-то это сказал, это чепуха?
После авторского эксперимента я нашел некоторые правила, продолжаем спускаться по этому правилу.
- При вращении оси X смотрите одновременно на градус текущей оси Z, ось Z нечетно кратна 90 градусам, вращайте ось Y, в противном случае вращайте ось X.
- При вращении оси Y смотрите одновременно на градус текущей оси Z, ось Z нечетно кратна 90 градусам, вращайте ось X, в противном случае вращайте ось Z.
- При вращении оси Z продолжайте вращать ось Z.
Таким образом, наше направление вращения фиксируется.
if (nextRotateDir[0] == "X") {
if (Math.floor(Math.abs(lastRotate.lastRotateZ) / 90) % 2 == 1) {
lastTransform[`rotateY`] = (lastRotate[`lastRotateY`] + 90 * dir) + 'deg';
} else {
lastTransform[`rotateX`] = (lastRotate[`lastRotateX`] - 90 * dir) + 'deg';
}
}
if (nextRotateDir[0] == "Y") {
if (Math.floor(Math.abs(Math.abs(lastRotate.lastRotateZ)) / 90) % 2 == 1) {
lastTransform[`rotateX`] = (lastRotate[`lastRotateX`] + 90 * dir) + 'deg';
} else {
lastTransform[`rotateZ`] = (lastRotate[`lastRotateZ`] + 90 * dir) + 'deg';
}
}
if (nextRotateDir[0] == "Z") {
lastTransform[`rotate${nextRotateDir[0]}`] = (lastRotate[`lastRotate${nextRotateDir[0]}`] - 90 * dir) + 'deg';
}
Однако на этом еще не все, в этом способе поворота еще есть яма, то есть я не знаю, то ли поворачивать на 90 градусов, то ли на -90 градусов.
Это не простое сложение и вычитание вверх, вниз, влево и вправо.
Направление вращения правильное, но я не знаю, как рассчитать угол поворота.
Конкретный код можно посмотретьисходный код.
яйцо время
⚠️⚠️⚠️ При этом будет сопровождаться "Универсальный замок», то есть ось Z совпадает с осью X. Хахахаха~
⚠️⚠️⚠️ Автор еще не решил ее, и я надеюсь, что всесильные нетизены смогут высказаться и помочь~
⚠️⚠️⚠️ Автор обновит позже.Хахахаха, большая яма.
Ну, эта проблема не затрагивает наш проект. Переходим к тому, как найти кратчайший путь и дать подсказки.
расчет кратчайшего пути
Как рассчитать кратчайший путь из одной точки в другую в лабиринте? Здесь я использую обход в ширину (BFS) алгоритм расчета кратчайшего пути.
Давайте подумаем о:
- Найдите кратчайший путь в двумерном массиве
- Кратчайший путь каждой сетки — это только четыре соседние сетки.
- Затем просто рекурсивно найдите кратчайшее расстояние каждой сетки, пока не найдете конечную точку.
Здесь нам нужно использовать "очередь«Функция «первым пришел – первым вышел».
Сначала посмотрим на картинку:
Ясно, что можно найти кратчайший путь.
Уведомление
Используйте два массива длины 4 для представления смещений нижних индексов, которые необходимо добавить к соседним сеткам.
Каждый раз, когда вы присоединяетесь к команде, вам нужно определить, присоединились ли вы уже к команде.
Каждый раз, когда вы покидаете команду, вам нужно судить, является ли это конечной точкой.
Необходимо записать родительский узел текущей поставленной в очередь цели, чтобы облегчить получение кратчайшего пути.
Давайте посмотрим на код:
//春初路径
var stack = [];
/**
* BFS 实现寻路
* @param {*} grid
* @param {*} start {x: 0,y: 0}
* @param {*} end {x: 3,y: 3}
*/
function getShortPath(grid, start, end, a) {
let maxL_x = grid.length;
let maxL_y = grid[0].length;
let queue = new Queue();
//最短步数
let step = 0;
//上左下右
let dx = [1, 0, -1, 0];
let dy = [0, 1, 0, -1];
//加入第一个元素
queue.enqueue(start);
//存储一个一样的用来排查是否遍历过
let mem = new Array(maxL_x);
for (let n = 0; n < maxL_x; n++) {
mem[n] = new Array(maxL_y);
mem[n].fill(100);
}
while (!queue.isEmpty()) {
let p = [];
for (let i = queue.size(); i > 0; i--) {
let preTraget = queue.dequeue();
p.push(preTraget);
//找到目标
if (preTraget.x == end.x && preTraget.y == end.y) {
stack.push(p);
return step;
}
//遍历四个相邻格子
for (let j = 0; j < 4; j++) {
let nextX = preTraget.x + dx[j];
let nextY = preTraget.y + dy[j];
if (nextX < maxL_x && nextX >= 0 && nextY < maxL_y && nextY >= 0) {
let nextTraget = { x: nextX, y: nextY };
if (grid[nextX][nextY] == a && a < mem[nextX][nextY]) {
queue.enqueue({ ...nextTraget, f: { x: preTraget.x, y: preTraget.y } });
mem[nextX][nextY] = a;
}
}
}
}
stack.push(p);
step++;
}
}
/* 找出一条最短路径**/
function recall(end) {
let path = [];
let front = { x: end.x, y: end.y };
while (stack.length) {
let item = stack.pop();
for (let i = 0; i < item.length; i++) {
if (!item[i].f) break;
if (item[i].x == front.x && item[i].y == front.y) {
path.push({ x: item[i].x, y: item[i].y });
front.x = item[i].f.x;
front.y = item[i].f.y;
break;
}
}
}
return path;
}
Таким образом, мы можем найти кратчайший путь и получить кратчайшее количество шагов.
Затем мы продолжаем обход нашего исходного массива (то есть исходного массива шахматной доски).
Нажмите на подсказку, чтобы осветить путь.
var step = getShortPath(panArr, { x: 0, y: 0 }, { x: 14, y: 14 }, 0);
console.log("最短距离----", step);
_perstep.innerHTML = `请在<span>${step}</span>步内走到终点`;
var path = recall({ x: 14, y: 14 });
console.log("路径---", path);
/** 提示 */
var tipCount = 0;
_tip.addEventListener("click", () => {
console.log("9999", tipCount)
elArr.forEach((item, index) => {
let r = Math.floor(index / 15);
let c = index % 15;
path.forEach((_item, i) => {
if (_item.x == r && _item.y == c) {
// console.log("ooo",_item)
if (tipCount % 2 == 0)
item.classList = "pan-item pan-path";
else
item.classList = "pan-item";
}
})
});
tipCount++;
});
Таким образом, мы можем получить подсказку, как показано на рисунке:
Вы закончили Эй, разве это не удивительно~
конец
Конечно, в этой моей маленькой игре еще есть место для улучшения. Например:
- Вы можете увеличить реквизит, а подобрать можно уменьшить количество пройденных шагов
- Можно добавить уровни конфигурации
- Также можно добавить функцию прыжка
- ...
Оказывается, есть еще много вещей, которые может CSS3D, и как его использовать, зависит от того, насколько богато ваше воображение.
Ха-ха-ха, я действительно хочу использовать CSS3D, чтобы написать "мой Мир«Поигравшись, проблема с производительностью может быть немного большой.
Все примеры в этой статье испытаны на ПК.
Приветствую всех на поправки, мастерство автора пока неглубоко, если есть что не так прошу поправить!
Статья поверхностная, надеюсь, вы не стесняетесь комментировать и ставить лайки~
Примечание: Данная статья является кропотливой авторской работой, перепечатки должны быть заявлены