На протяжении некоторого времени я создаю эти 3D сцены с помощью CSS для развлечения — обычно на моем прямом эфире.
3D CSS Реактивный самолет-трюк! 🛩️
Он делает петли и перекаты! 😎
Отвечает на движение мыши 🐭
👉 https://t.co/A1zNmfEzzi через @CodePen pic.twitter.com/87D7LIXLr2
— Jhey 🐻🛠 (Exploring Opportunities ✨) (@jh3yy) March 27, 2021
Каждый демо-пример — это возможность попробовать что-то новое или найти способы выполнять задачи с помощью CSS. Одно из моих обычных занятий — принимать предложения о том, что мы должны попробовать сделать на стриме. Недавно было предложение создать принтер, который печатает в “3D”. И вот что я придумал!
📢 CSS Print Shop открыт для бизнеса! 😅
Бесплатная печать! 😮
Введите URL изображения, чтобы увидеть его распечатанным на 3D CSS принтере 😁
👉 https://t.co/UWTDAyUadn через @CodePen pic.twitter.com/z3q9dJavYv
— Jhey 🐻🛠 (Exploring Opportunities ✨) (@jh3yy) April 22, 2021
Создание 3D объектов с помощью CSS
I’ve written about making things 3D with CSS before. The general gist is that most scenes are a composition of cuboids.
Чтобы создать куб, мы можем использовать CSS трансформации для позиционирования сторон куба — волшебное свойство — transform-style
. Установка этого свойства в preserve-3d
позволяет нам трансформировать элементы в третьем измерении:
* {
transform-style: preserve-3d;
}
Как только вы создадите несколько таких сцен, вы начнете находить способы ускорить процесс. Мне нравится использовать Pug в качестве препроцессора HTML. Возможность создания миксинов позволяет мне быстрее создавать кубики. В примерах разметки в этой статье используется Pug. Но для каждого демо CodePen вы можете использовать опцию “View Compiled HTML”, чтобы увидеть выход HTML:
mixin cuboid()
.cuboid(class!=attributes.class)
- let s = 0
while s < 6
.cuboid__side
- s++
Использование +cuboid()(class="printer__top")
даст следующее:
<div class="cuboid printer__top">
<div class="cuboid__side"></div>
<div class="cuboid__side"></div>
<div class="cuboid__side"></div>
<div class="cuboid__side"></div>
<div class="cuboid__side"></div>
<div class="cuboid__side"></div>
</div>
Затем у меня есть блок CSS, который я использую для размещения кубиков. Радость здесь в том, что мы можем использовать CSS-свойства для определения свойств кубика (как показано в видео выше):
.cuboid {
// Defaults
--width: 15;
--height: 10;
--depth: 4;
height: calc(var(--depth) * 1vmin);
width: calc(var(--width) * 1vmin);
transform-style: preserve-3d;
position: absolute;
font-size: 1rem;
transform: translate3d(0, 0, 5vmin);
}
.cuboid > div:nth-of-type(1) {
height: calc(var(--height) * 1vmin);
width: 100%;
transform-origin: 50% 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotateX(-90deg) translate3d(0, 0, calc((var(--depth) / 2) * 1vmin));
}
.cuboid > div:nth-of-type(2) {
height: calc(var(--height) * 1vmin);
width: 100%;
transform-origin: 50% 50%;
transform: translate(-50%, -50%) rotateX(-90deg) rotateY(180deg) translate3d(0, 0, calc((var(--depth) / 2) * 1vmin));
position: absolute;
top: 50%;
left: 50%;
}
.cuboid > div:nth-of-type(3) {
height: calc(var(--height) * 1vmin);
width: calc(var(--depth) * 1vmin);
transform: translate(-50%, -50%) rotateX(-90deg) rotateY(90deg) translate3d(0, 0, calc((var(--width) / 2) * 1vmin));
position: absolute;
top: 50%;
left: 50%;
}
.cuboid > div:nth-of-type(4) {
height: calc(var(--height) * 1vmin);
width: calc(var(--depth) * 1vmin);
transform: translate(-50%, -50%) rotateX(-90deg) rotateY(-90deg) translate3d(0, 0, calc((var(--width) / 2) * 1vmin));
position: absolute;
top: 50%;
left: 50%;
}
.cuboid > div:nth-of-type(5) {
height: calc(var(--depth) * 1vmin);
width: calc(var(--width) * 1vmin);
transform: translate(-50%, -50%) translate3d(0, 0, calc((var(--height) / 2) * 1vmin));
position: absolute;
top: 50%;
left: 50%;
}
.cuboid > div:nth-of-type(6) {
height: calc(var(--depth) * 1vmin);
width: calc(var(--width) * 1vmin);
transform: translate(-50%, -50%) translate3d(0, 0, calc((var(--height) / 2) * -1vmin)) rotateX(180deg);
position: absolute;
top: 50%;
left: 50%;
}
Используя пользовательские свойства, мы можем контролировать различные характеристики кубиков и т. д.:
--width
: ширина кубика на плоскости--height
: высота кубика на плоскости--depth
: глубина кубика на плоскости--x
: позиция X на плоскости--y
: позиция Y на плоскости
Это не очень впечатляет, пока мы не поместим кубик в сцену и не повернем его. Опять же, я использую пользовательские свойства, чтобы управлять сценой, пока работаю над чем-то. Dat.GUI здесь очень удобен.
Если вы проверите демо, используя панель управления, обновляются пользовательские CSS-свойства на сцене. Этот объем CSS-свойств экономит много повторяющегося кода и сохраняет все DRY.
Больше одного способа
Так же, как и многие вещи в CSS, есть несколько способов это сделать. Часто можно составить сцену из кубиков и позиционировать объекты по мере необходимости. Однако это может быть довольно сложно управлять. Часто возникает необходимость группировать объекты или добавлять какой-то тип контейнера.
Рассмотрим этот пример, где стул представляет собой свою собственную подсцену, которую можно перемещать.
Многие недавние примеры не так сложны. Я часто использую экструзию. Это означает, что я могу отобразить все, что делаю, в 2D элементах. Например, вот вертолет, который я недавно создал:
.helicopter
.helicopter__rotor
.helicopter__cockpit
.helicopter__base-light
.helicopter__chair
.helicopter__chair-back
.helicopter__chair-bottom
.helicopter__dashboard
.helicopter__tail
.helicopter__fin
.helicopter__triblade
.helicopter__tail-light
.helicopter__stabilizer
.helicopter__skids
.helicopter__skid--left.helicopter__skid
.helicopter__skid--right.helicopter__skid
.helicopter__wing
.helicopter__wing-light.helicopter__wing-light--left
.helicopter__wing-light.helicopter__wing-light--right
.helicopter__launchers
.helicopter__launcher.helicopter__launcher--left
.helicopter__launcher.helicopter__launcher--right
.helicopter__blades
Затем мы можем поместить кубики во все контейнеры, используя миксин. Затем применить необходимую “толщину” к каждому кубику. Толщина определяется с помощью скоупированных пользовательских свойств. Этот демо-пример переключает свойство --thickness
для кубиков, составляющих вертолет. Это дает представление о том, как выглядела 2D-карта в начале.
Вот вкратце, как создавать 3D объекты с помощью CSS. Погружение в код, безусловно, раскроет некоторые хитрости. Однако, в общем, сначала скелет сцены, заполнение кубиками и окраска кубиков. Часто нужно использовать разные оттенки цвета, чтобы мы могли различать стороны кубика. Любые дополнительные детали либо можно добавить на сторону кубика, либо трансформации, которые можно применить к кубику. Например, вращение и перемещение по оси Z.
Давайте рассмотрим упрощенный пример:
.scene
.extrusion
+cuboid()(class="extrusion__cuboid")
Новый CSS для создания кубика с экструзией может выглядеть следующим образом. Обратите внимание, как мы включаем скоупированные пользовательские свойства для цвета каждой стороны тоже. Было бы разумно добавить некоторые значения по умолчанию под :root
здесь или резервные значения:
.cuboid {
width: 100%;
height: 100%;
position: relative;
}
.cuboid__side:nth-of-type(1) {
background: var(--shade-one);
height: calc(var(--thickness) * 1vmin);
width: 100%;
position: absolute;
top: 0;
transform: translate(0, -50%) rotateX(90deg);
}
.cuboid__side:nth-of-type(2) {
background: var(--shade-two);
height: 100%;
width: calc(var(--thickness) * 1vmin);
position: absolute;
top: 50%;
right: 0;
transform: translate(50%, -50%) rotateY(90deg);
}
.cuboid__side:nth-of-type(3) {
background: var(--shade-three);
width: 100%;
height: calc(var(--thickness) * 1vmin);
position: absolute;
bottom: 0;
transform: translate(0%, 50%) rotateX(90deg);
}
.cuboid__side:nth-of-type(4) {
background: var(--shade-two);
height: 100%;
width: calc(var(--thickness) * 1vmin);
position: absolute;
left: 0;
top: 50%;
transform: translate(-50%, -50%) rotateY(90deg);
}
.cuboid__side:nth-of-type(5) {
background: var(--shade-three);
height: 100%;
width: 100%;
transform: translate3d(0, 0, calc(var(--thickness) * 0.5vmin));
position: absolute;
top: 0;
left: 0;
}
.cuboid__side:nth-of-type(6) {
background: var(--shade-one);
height: 100%;
width: 100%;
transform: translate3d(0, 0, calc(var(--thickness) * -0.5vmin)) rotateY(180deg);
position: absolute;
top: 0;
left: 0;
}
Мы выбрали три оттенка для этого примера. Но иногда может потребоваться больше. Этот демо-материал объединяет это, но позволяет изменять скоупированные пользовательские свойства. Значение “толщины” изменит выдавливание кубоида. Преобразования и размеры будут влиять на содержащий элемент с классом “выдавливание”.
Скейфолдинг принтера
Для начала мы можем скейфолдить все детали, которые нам нужны. С опытом это становится более очевидным. Но общее правило – попытаться визуализировать все как коробки. Это дает вам хорошее представление о том, как что-то разбить:
.scene
.printer
.printer__side.printer__side--left
.printer__side.printer__side--right
.printer__tray.printer__tray--bottom
.printer__tray.printer__tray--top
.printer__top
.printer__back
Посмотрите, сможете ли вы визуализировать то, что мы здесь пытаемся сделать. Две боковые части оставляют промежуток посередине. Затем у нас есть кубоид, который располагается поверх и один, который заполняет заднюю часть. Затем два кубоида, составляющие лот для бумаги.
Как только вы дойдете до этой стадии, дело заключается в заполнении кубоидов, что выглядит следующим образом:
.scene
.printer
.printer__side.printer__side--left
+cuboid()(class="cuboid--side")
.printer__side.printer__side--right
+cuboid()(class="cuboid--side")
.printer__tray.printer__tray--bottom
+cuboid()(class="cuboid--tray")
.printer__tray.printer__tray--top
+cuboid()(class="cuboid--tray")
.printer__top
+cuboid()(class="cuboid--top")
.printer__back
+cuboid()(class="cuboid--back")
Обратите внимание, как мы можем повторно использовать имена классов, такие как cuboid--side
. Эти кубоиды, вероятно, будут иметь одинаковую толщину и использовать одни и те же цвета. Их положение и размер определяются содержащим элементом.
Собирая все вместе, мы можем получить что-то вроде этого.
Взрывая демо, можно увидеть разные кубоиды, которые составляют принтер. Если вы выключите выдавливание, вы увидите плоские содержащие элементы.
Добавление некоторой детали
Теперь, вы, возможно, заметили, что есть больше деталей, чем можно получить, просто добавив цвета на каждую сторону. И это сводится к поиску способов добавления дополнительных деталей. У нас есть разные варианты в зависимости от того, что мы хотим добавить.
Если это изображение или какие-то простые изменения цвета, мы можем использовать `background-image
` для наложения градиентов и т.д.
Например, верх принтера имеет детали, а также открытие принтера. Этот код касается верхней стороны верхнего параллелепипеда. Градиент обрабатывает открытие принтера и детали:
.cuboid--top {
--thickness: var(--depth);
--shade-one: linear-gradient(#292929, #292929) 100% 50%/14% 54% no-repeat, linear-gradient(var(--p-7), var(--p-7)) 40% 50%/12% 32% no-repeat, linear-gradient(var(--p-7), var(--p-7)) 30% 50%/2% 12% no-repeat, linear-gradient(var(--p-3), var(--p-3)) 0% 50%/66% 50% no-repeat, var(--p-1);
}
Для логотипа медведя мы могли бы использовать `background-image
` или даже обратиться к псевдоэлементу и позиционировать его:
.cuboid--top > div:nth-of-type(1):after {
content: '';
position: absolute;
top: 7%;
left: 10%;
height: calc(var(--depth) * 0.12vmin);
width: calc(var(--depth) * 0.12vmin);
background: url("https://assets.codepen.io/605876/avatar.png");
background-size: cover;
transform: rotate(90deg);
filter: grayscale(0.5);
}
Если нам нужно добавить более обширные детали, нам, вероятно, придется выйти за рамки использования нашего миксина параллелепипеда. Например, верх нашего принтера будет иметь предварительный просмотр экрана с использованием элемента `img
`:
.printer__top
.cuboid.cuboid--top
.cuboid__side
.cuboid__side
.cuboid__side
.cuboid__side
.screen
.screen__preview
img.screen__preview-img
.cuboid__side
.cuboid__side
Добавьте еще несколько деталей, и мы готовы добавить бумагу в смесь!
Путешествие по бумаге
Что такое принтер без бумаги? Мы хотим анимировать полет бумаги в принтер и выброс ее из другого конца. Что-то вроде этого демо: нажмите куда угодно, чтобы увидеть лист бумаги, который вставляется в принтер и печатается.
Мы можем добавить блок бумаги в сцену с параллелепипедом, а затем использовать отдельный элемент для представления одного листа бумаги:
.paper-stack.paper-stack--bottom
+cuboid()(class="cuboid--paper")
.paper-stack.paper-stack--top
.cuboid.cuboid--paper
.cuboid__side
.paper
.paper__flyer
.cuboid__side
.cuboid__side
.cuboid__side
.cuboid__side
.cuboid__side
Но анимировать полет бумаги в принтер требует некоторого экспериментирования. Разумно поиграть с различными трансформациями в инспекторе DevTools. Это хороший способ увидеть, как все будет выглядеть. Часто бывает проще использовать оберточные элементы тоже. Мы используем элемент `.paper
` для передачи, а затем используем `.paper__flyer
` для анимации подачи бумаги:
:root {
--load-speed: 2;
}
.paper-stack--top .cuboid--paper .paper {
animation: transfer calc(var(--load-speed) * 0.5s) ease-in-out forwards;
}
.paper-stack--top .cuboid--paper .paper__flyer {
animation: fly calc(var(--load-speed) * 0.5s) ease-in-out forwards;
}
.paper-stack--top .cuboid--paper .paper__flyer:after {
animation: feed calc(var(--load-speed) * 0.5s) calc(var(--load-speed) * 0.5s) forwards;
}
@keyframes transfer {
to {
transform: translate(0, -270%) rotate(22deg);
}
}
@keyframes feed {
to {
transform: translate(100%, 0);
}
}
@keyframes fly {
0% {
transform: translate3d(0, 0, 0) rotateY(0deg) translate(0, 0);
}
50% {
transform: translate3d(140%, 0, calc(var(--height) * 1.2)) rotateY(-75deg) translate(180%, 0);
}
100% {
transform: translate3d(140%, 0, var(--height)) rotateY(-75deg) translate(0%, 0) rotate(-180deg);
}
}
Вы заметите, что там довольно много использования calc
. Для составления временной шкалы анимации мы можем воспользоваться пользовательскими свойствами CSS. Обращаясь к свойству, мы можем рассчитать правильные задержки для каждой анимации в цепочке. Бумага переносится и летит одновременно. Одна анимация управляет перемещением контейнера, другая – вращением бумаги. Как только эти анимации заканчиваются, бумага подается в принтер с помощью анимации feed
. Задержка анимации равна продолжительности первых двух анимаций, которые запускаются одновременно.
Запустите этот демонстрационный пример, где я окрасил элементы контейнеров в красный и зеленый цвета. Мы используем псевдоэлемент .paper__flyer
для представления листа бумаги. Но элементы контейнеров выполняют основную работу:
Возможно, вам интересно, когда бумага выходит с другого конца. Но на самом деле бумага не является одним и тем же элементом на протяжении всего процесса. Мы используем один элемент, чтобы войти в принтер. И другой элемент для бумаги, когда она вылетает из принтера. Это еще одно место, где дополнительные элементы облегчают нам жизнь.
Бумага использует более одного элемента для выполнения петли, а затем бумага позиционируется на краю этого элемента. Запустив этот демонстрационный пример с большим количеством окрашенных элементов контейнеров, вы увидите, как это работает.
Опять же, это требует некоторого проб и ошибок, а также размышлений о том, как мы можем использовать элементы контейнеров. Имея контейнер с смещенной transform-origin
, мы можем создать петлю.
Печать
У нас все готово. Теперь дело за тем, чтобы на самом деле что-то распечатать. Для этого мы добавим форму, которая позволит пользователям передавать URL изображения:
form.customer-form
label(for="print") Print URL
input#print(type='url' required placeholder="URL for Printing")
input(type="submit" value="Print")
С некоторым оформлением, мы получим что-то вроде этого.
Природа форм и использование required
и type="url"
означает, что мы принимаем только URL. Мы могли бы продвинуться дальше с помощью pattern
и проверки определенных типов изображений. Но некоторые хорошие URL для случайных изображений не включают тип изображения, например https://source.unsplash.com/random.
Отправка нашей формы не ведет себя так, как мы хотим, и также анимация печати запускается один раз при загрузке. Обойти это можно, запуская анимацию только тогда, когда к принтеру применяется определенный класс.
Когда мы отправляем форму, мы можем запросить URL и затем установить src
для изображений в нашей сцене – одно изображение будет экраном предварительного просмотра на принтере, другое – изображением на одной стороне бумаги. Фактически, когда мы печатаем, мы собираемся добавить новый элемент для каждой распечатанной части бумаги. Таким образом, каждая печать выглядит так, как будто она добавляется в стопку. Мы можем удалить часть бумаги, которая у нас есть при загрузке.
Начнем с обработки отправки формы. Мы собираемся предотвратить дефолтное событие и вызвать функцию PROCESS
:
const PRINT = e => {
e.preventDefault()
PROCESS()
}
const PRINT_FORM = document.querySelector('form')
PRINT_FORM.addEventListener('submit', PRINT)
Эта функция будет обрабатывать запрос для нашего источника изображения:
let printing = false
const PREVIEW = document.querySelector('img.screen__preview-img')
const SUBMIT = document.querySelector('[type="submit"]')
const URL_INPUT = document.querySelector('[type="url"]')
const PROCESS = async () => {
if (printing) return
printing = true
SUBMIT.disabled = true
const res = await fetch(URL_INPUT.value)
PREVIEW.src = res.url
URL_INPUT.value = ''
}
Мы также установили переменную printing
в true
, которую будем использовать для отслеживания текущего состояния и отключения кнопки формы.
Почему мы делаем запрос на изображение, а не устанавливаем его на изображении? Нам нужен абсолютный URL изображения. Если мы используем упомянутый выше URL “Unsplash” и затем разделяем его между изображениями, это может не сработать. Это потому, что мы можем столкнуться со сценариями, когда отображаются разные изображения.
Как только у нас появляется источник изображения, мы устанавливаем источник превью изображения на этот URL и сбрасываем значение входного значения формы.
Чтобы запустить анимацию, мы можем подключиться к событию “load” нашего превью изображения. Когда событие срабатывает, мы создаем новый элемент для отпечатка листа бумаги и добавляем его в элемент printer
. В то же время мы добавляем класс printing
к нашему принтеру. Мы можем использовать это для запуска первой части анимации бумаги:
PREVIEW.addEventListener('load', () => {
PRINTER.classList.add('printing')
const PRINT = document.createElement('div')
PRINT.className = 'printed'
PRINT.innerHTML = `
<div class="printed__spinner">
<div class="printed__paper">
<div class="printed__papiere">
<img class="printed__image" src=${PREVIEW.src}/>
</div>
</div>
<div class="printed__paper-back"></div>
</div>
`
PRINTER.appendChild(PRINT)
// После установленного количества времени сбросить состояние
setTimeout(() => {
printing = false
SUBMIT.removeAttribute('disabled')
PRINTER.classList.remove('printing')
}, 4500)
})
После установленного количества времени мы можем сбросить состояние. Альтернативный подход заключался бы в задержке всплывающего события animationend
. Но мы можем использовать setTimeout
, так как знаем, сколько времени займет анимация.
Однако наш печатный процесс не в правильном масштабе. И это потому, что нам нужно масштабировать изображение до размеров листа бумаги. Для этого нам нужна небольшая часть CSS:
.printed__image {
height: 100%;
width: 100%;
object-fit: cover;
}
Было бы здорово, если бы лампы на передней части принтера сообщали, что принтер занят. Мы могли бы изменить оттенок одной из ламп, когда принтер печатает:
.progress-light {
background: hsla(var(--progress-hue, 104), 80%, 50%);
}
.printing {
--progress-hue: 10; /* Соответствует красному цвету */
}
Соберите всё вместе, и у нас получится “работающий” принтер, созданный с использованием CSS и крошечного количества JavaScript.
И это всё!
Мы рассмотрели, как можно создать функциональный 3D принтер с помощью CSS, небольшого количества JavaScript и используя Pug. Попробуйте добавить следующий ссылку на изображение в поле URL или любую другую по своему выбору и возьмите его на тест!
https://source.unsplash.com/random
Мы рассмотрели множество различных вещей для достижения этого, включая следующее:
- как создавать 3D объекты с помощью CSS
- использование миксинов Pug
- использование ограниченных пользовательских CSS-свойств для сохранения DRY
- использование экструзии для создания 3D сцен
- обработка форм с помощью JavaScript
- композиция временных шкал анимации с использованием пользовательских свойств
Радость создания этих демонстраций заключается в том, что многие из них ставят перед собой разные проблемы, которые нужно преодолеть, такие как создание определенных форм или создание определенных анимаций. Часто есть несколько способов сделать что-то.
Какие классные вещи вы могли бы сделать с 3D CSS? Я бы хотел увидеть!
Как всегда, спасибо за чтение. Хочешь увидеть больше? Приходи ко мне на Twitter или посмотри мою прямую трансляцию!
Часто задаваемые вопросы (FAQ) о 3D CSS Printer
Что такое 3D CSS Printer?
A 3D CSS Printer is a unique concept that uses Cascading Style Sheets (CSS), a style sheet language used for describing the look and formatting of a document written in HTML, to create a 3D representation of a printer. This innovative approach allows developers to create interactive and visually appealing web elements that can enhance user experience.
Как работает 3D CSS Printer?
A 3D CSS Printer works by using CSS properties to create a 3D model of a printer. It uses properties such as transform, perspective, and animation to create the 3D effect. The printer is made up of multiple elements, each styled and positioned to create the overall 3D effect. The animation property is then used to create the printing effect.
Могу ли я настроить 3D CSS Printer?
Да, вы можете настроить 3D CSS Printer. Вы можете изменить цвета, размер и даже скорость анимации. Это достигается путем изменения CSS-свойств принтера. Например, вы можете изменить цвет, изменив свойство background-color элементов принтера.
Как я могу интегрировать 3D CSS Printer в свой веб-сайт?
Интеграция 3D CSS Printer в ваш веб-сайт включает в себя копирование CSS и HTML кода в код вашего веб-сайта. Вам нужно будет убедиться, что CSS-код включен в раздел head вашего HTML-документа, а HTML-код размещен там, где вы хотите, чтобы принтер появился на вашей веб-странице.
Возможно ли анимировать 3D CSS Printer?
Да, возможно анимировать 3D CSS Printer. Анимация достигается с помощью свойства CSS animation. Это свойство позволяет создавать ключевые кадры, которые определяют начальное и конечное состояния анимации, а также любые промежуточные шаги.
Какие браузеры поддерживают 3D CSS Printer?
3D CSS Printer должен работать на всех современных браузерах, которые поддерживают свойства CSS transform и animation. К ним относятся браузеры, такие как Google Chrome, Mozilla Firefox, Safari и Microsoft Edge.
Могу ли я использовать 3D CSS Printer для коммерческих целей?
Да, вы можете использовать 3D CSS Printer для коммерческих целей. Однако всегда полезно проверить условия лицензирования любого используемого кода, чтобы убедиться, что вы соблюдаете их.
Какие навыки мне нужны для создания 3D CSS Printer?
Для создания 3D принтера на CSS вам потребуется хорошее понимание HTML и CSS. Вы должны быть знакомы с такими свойствами CSS, как transform, perspective и animation. Базовые знания 3D-моделирования также могут быть полезны, но не обязательны.
Могу ли я использовать JavaScript с 3D CSS Printer?
Да, вы можете использовать JavaScript с 3D CSS Printer. Хотя принтер можно создать, используя только CSS, JavaScript можно использовать для добавления интерактивности, такой как запуск или остановка анимации в зависимости от действий пользователя.
Есть ли ресурсы для изучения 3D CSS Printer?
Существует множество онлайн-ресурсов для изучения 3D CSS Printer. Веб-сайты, такие как SitePoint, CSS-Tricks и MDN Web Docs, содержат обширные руководства и пособия по CSS-анимациям и 3D-трансформациям. На YouTube также есть множество видеоуроков по этой теме.