A quick play with some 3D CSS and animation.
Will add some more animation to this when I have more time
A Pen by Peter Norton on CodePen.
| // Lil minecraft wolf | |
| // source: | |
| // https://minecraft.fandom.com/wiki/Wolf | |
| // Inspired by the works of Grant Jenkins: | |
| // https://codepen.io/grantjenkins/pen/ZExEOex?editors=0100 | |
| mixin box(name) | |
| .box(class=name) | |
| .lt | |
| .rt | |
| .tp | |
| .bt | |
| .ft | |
| .bk | |
| .scene | |
| +box('mud') | |
| +box('grass') | |
| .shadow | |
| .wolf | |
| .legs | |
| +box('leg lt bk') | |
| +box('leg lt ft') | |
| +box('leg rt bk') | |
| +box('leg rt ft') | |
| .tails | |
| +box('tail') | |
| .bodys | |
| +box('body ft') | |
| +box('body bk') | |
| .heads | |
| +box('head') | |
| +box('snout') | |
| .mouths | |
| +box('mouth') | |
| +box('ear lt') | |
| +box('ear rt') | |
A quick play with some 3D CSS and animation.
Will add some more animation to this when I have more time
A Pen by Peter Norton on CodePen.
| // Enable scene interaction | |
| const scene = document.querySelector('.scene'); | |
| const rotation = []; | |
| const startPos = []; | |
| let scale = 1; | |
| const start = event => { | |
| // get the rx and ry of scene | |
| let style = window.getComputedStyle(scene); | |
| rotation[0] = parseInt(style.getPropertyValue('--rx')); | |
| rotation[1] = parseInt(style.getPropertyValue('--ry')); | |
| // mouse starting position | |
| startPos[0] = event.clientX || event.touches[0].clientX; | |
| startPos[1] = event.clientY || event.touches[0].clientY; | |
| }; | |
| const update = event => { | |
| if (startPos.length > 0) { | |
| // track changes to the x and y | |
| let x = (event.clientX || event.touches[0].clientX) - startPos[0]; | |
| let y = (event.clientY || event.touches[0].clientY) - startPos[1]; | |
| // update the scene | |
| scene.style.setProperty('--rx', rotation[0] - y + 'deg'); | |
| scene.style.setProperty('--ry', rotation[1] + x + 'deg'); | |
| // log output | |
| console.table({ | |
| rx: `${rotation[0] - y}deg`, | |
| ry: `${rotation[1] - x}deg` | |
| }); | |
| } | |
| }; | |
| const end = event => { | |
| startPos.length = 0; | |
| }; | |
| scene.addEventListener('touchstart', start, false); | |
| scene.addEventListener('mousedown', start, false); | |
| document.addEventListener('touchmove', update, false); | |
| document.addEventListener('mousemove', update, false); | |
| document.addEventListener('touchend', end, false); | |
| document.addEventListener('mouseup', end, false); | |
| // zoom in out on mouse wheel | |
| scene.addEventListener('wheel', event => { | |
| event.preventDefault(); | |
| scale += event.deltaY * -0.01; | |
| scale = scale > 3 ? 3 : scale < 0.25 ? 0.25 : scale; | |
| scene.style.setProperty('--scene-scale', scale); | |
| }, { passive: false }); |
| // Cube | |
| // Sizing: --w: width, --h: height, --d: depth | |
| // Position: --x: x-axis translation, --y: y-axis translation, --z: z-axis translation | |
| .box { | |
| --x: 0; // default | |
| --y: 0; // default | |
| --z: 0; // default | |
| --d2: calc(var(--d) / 2); | |
| position: absolute; | |
| width: var(--w); | |
| height: var(--h); | |
| transform: translate3d(var(--x), var(--y), var(--z)); | |
| div { | |
| position: absolute; | |
| // outline: 1px solid rgba(grey, .25); | |
| } | |
| .lt { | |
| width: var(--d); | |
| height: var(--h); | |
| left: 0; | |
| transform: rotateY(-90deg) translatez(var(--d2)); | |
| } | |
| .rt { | |
| width: var(--d); | |
| height: var(--h); | |
| right: 0; | |
| transform: rotateY(90deg) translatez(var(--d2)); | |
| } | |
| .tp { | |
| width: var(--w); | |
| height: var(--d); | |
| top: 0; | |
| transform: rotateX(90deg) translateZ(var(--d2)); | |
| } | |
| .bt { | |
| width: var(--w); | |
| height: var(--d); | |
| bottom: 0; | |
| transform: rotateX(-90deg) translateZ(var(--d2)); | |
| } | |
| .ft { | |
| width: var(--w); | |
| height: var(--h); | |
| transform: translateZ(var(--d2)); | |
| } | |
| .bk { | |
| width: var(--w); | |
| height: var(--h); | |
| transform: rotateY(180deg) translateZ(var(--d2)); | |
| } | |
| } | |
| @mixin color($col) { | |
| // lit from front-left | |
| .ft { background: $col } | |
| .tp { background: lighten($col, 8%) } | |
| .lt { background: lighten($col, 4%) } | |
| .bk { background: darken($col, 8%) } | |
| .rt { background: darken($col, 4%) } | |
| .bt { background: darken($col, 20%) } | |
| } | |
| @mixin vars($w: 0, $h: 0, $d: 0, $x: 0, $y: 0, $z: 0) { | |
| --w: calc(#{$w} * 1vmin); // width | |
| --h: calc(#{$h} * 1vmin); // height | |
| --d: calc(#{$d} * 1vmin); // depth | |
| --x: calc(#{$x} * 1vmin); // translate-x | |
| --y: calc(#{$y} * 1vmin); // translate-y | |
| --z: calc(#{$z} * 1vmin); // translate-z | |
| } | |
| // Setting up the scene | |
| :root { | |
| --scene-width: 60vmin; | |
| --scene-height: 20vmin; | |
| --scene-depth: 60vmin; | |
| --scene-scale: 1; | |
| } | |
| body { | |
| display: grid; | |
| place-items: center; | |
| min-height: 100vh; | |
| overflow: hidden; | |
| perspective: 1000px; | |
| background-image: radial-gradient(#c9d8f7, #3874f5); | |
| } | |
| .scene { | |
| --rx: -15deg; | |
| --ry: -55deg; | |
| position: relative; | |
| transform-style: preserve-3d; | |
| width: var(--scene-width); | |
| height: var(--scene-height); | |
| cursor: grab; | |
| transform: | |
| rotateX(var(--rx)) | |
| rotateY(var(--ry)) | |
| scale3d(var(--scene-scale), var(--scene-scale), var(--scene-scale)); | |
| * { | |
| box-sizing: border-box; | |
| transform-style: preserve-3d; | |
| } | |
| &:active { | |
| cursor: grabbing; | |
| } | |
| } | |
| // Ground | |
| .grass { | |
| --w: var(--scene-width); | |
| --d: var(--scene-depth); | |
| --h: 1vmin; | |
| --y: var(--scene-height); | |
| @include color(#659a42); | |
| } | |
| .mud { | |
| --w: var(--scene-width); | |
| --d: var(--scene-depth); | |
| --h: 4vmin; | |
| --y: calc(var(--scene-height) + 1vmin); | |
| @include color(#6a5040); | |
| } | |
| .shadow { | |
| width: 12vmin; | |
| height: 25vmin; | |
| position: absolute; | |
| background: rgba(black, .1); | |
| left: 24vmin; | |
| transform: rotatex(90deg) translatez(-7.5vmin); | |
| filter: blur(4px); | |
| } | |
| // Wolf | |
| .wolf { | |
| transform: translateX(25vmin) translateY(2vmin); | |
| @include color(#999797); | |
| .tail { | |
| // vars= (w, h, d, x, y, x) | |
| @include vars(3, 3, 10, 3.5, 2, -12); | |
| } | |
| .leg { | |
| @include vars(3, 8, 3, 1, 10, 7); | |
| &.rt { | |
| --x: 6vmin; | |
| } | |
| &.bk { | |
| --z: -7vmin; | |
| } | |
| } | |
| .body { | |
| &.ft { | |
| @include vars(11, 10, 8, -0.5, 0, 6); | |
| @include color(#8f8689); | |
| .ft { | |
| background: #a71c14; | |
| } | |
| } | |
| &.bk { | |
| @include vars(8, 8, 12, 1, 2, -4); | |
| } | |
| } | |
| .head { | |
| @include vars(9, 8, 4, 0.5, 1, 12); | |
| .ft::before, .ft::after { | |
| content: ''; | |
| position: absolute; | |
| background: black; | |
| width: 1.5vmin; | |
| height: 2vmin; | |
| left: 2vmin; | |
| top: 2vmin; | |
| } | |
| .ft::after { | |
| left: 5.5vmin; | |
| } | |
| } | |
| .snout { | |
| @include color(#b8aaa8); | |
| @include vars(4, 2.5, 4, 3, 5, 16); | |
| .ft::before, | |
| .tp::before { | |
| content: ''; | |
| position: absolute; | |
| background: #32312d; | |
| width: 2vmin; | |
| height: 1vmin; | |
| left: 1vmin; | |
| top: 0; | |
| } | |
| .tp:before { | |
| top: unset; | |
| bottom: 0; | |
| height: 1.5vmin; | |
| } | |
| } | |
| .mouth { | |
| @include color(#32312d); | |
| @include vars(4, 1.5, 4, 3, 7.5, 16); | |
| } | |
| .ear { | |
| @include vars(3, 3, 1, 0.5, -2, 11); | |
| &.rt { | |
| --x: 6.5vmin; | |
| } | |
| > div:not(.ft) { | |
| background: #32312d; | |
| } | |
| } | |
| } | |
| // Animation | |
| .tails { | |
| transform-origin: 3.5vmin 2vmin -9vmin; | |
| animation: wag .5s ease-in-out infinite alternate; | |
| } | |
| .mouths { | |
| transform-origin: 3vmin 7.5vmin 14vmin; | |
| animation: bark 4s ease-in-out infinite alternate; | |
| } | |
| @keyframes wag { | |
| from { | |
| transform: rotateX(-20deg) rotateY(-25deg); | |
| } | |
| to { | |
| transform: rotateX(-20deg) rotateY(25deg); | |
| } | |
| } | |
| @keyframes bark { | |
| 0%, 20% { | |
| transform: rotateX(0deg); | |
| } | |
| 10% { | |
| transform: rotateX(-20deg); | |
| } | |
| } |