A Pen by Dimage Sapelkin on CodePen.
Created
June 3, 2021 15:23
-
-
Save kerabromsmu/3d90775547d67c159072815fe0f3cf2b to your computer and use it in GitHub Desktop.
Item adjust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.3/css/all.css" integrity="sha384-SZXxX4whJ79/gErwcOYf+zWLeJdY/qpuqC4cAa9rOGUstPomtqpuNWT9wdPEn2fk" crossorigin="anonymous"> | |
<audio id="click-sound" src="https://ddguo3wqwl0bj.cloudfront.net/content/5fa24e7d-c51b-42e2-b0ee-bd816d11d88c/5fa24e7d-c51b-42e2-b0ee-bd816d11d88c.ogg" crossorigin="anonymous"></audio> | |
<div class="scene-container" id="scene-container"> | |
<div class="draggable-scene" id="scene"> | |
<div class="floor"></div> | |
<div class="gun-pivot" id="gun-pivot"> | |
<div id="gun" class="gun"> | |
<div class="gun-flipper" id="gun-flipper"> | |
<div class="stock box"> | |
<div class="top metal"></div> | |
<div class="left metal"></div> | |
<div class="right metal"></div> | |
<div class="front metal"></div> | |
<div class="back metal"></div> | |
<div class="down metal"></div> | |
</div> | |
<div class="handle box"> | |
<div class="top metal"></div> | |
<div class="left metal"></div> | |
<div class="right metal"></div> | |
<div class="front metal"></div> | |
<div class="back metal"></div> | |
<div class="down metal"></div> | |
</div> | |
<div class="aim box"> | |
<div class="top metal"></div> | |
<div class="left metal"></div> | |
<div class="right metal"></div> | |
<div class="front metal"></div> | |
<div class="back metal"></div> | |
<div class="down metal"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div id="char" class="char"> | |
<div id="head" class="head box"> | |
<div class="top skin"></div> | |
<div class="left skin"></div> | |
<div class="right skin"></div> | |
<div class="front skin"> | |
<div class="face"></div> | |
</div> | |
<div class="back skin"></div> | |
<div class="down skin"></div> | |
</div> | |
<div id="body" class="body box"> | |
<div class="top skin"></div> | |
<div class="left skin"></div> | |
<div class="right skin"></div> | |
<div class="front skin"></div> | |
<div class="back skin"></div> | |
<div class="down skin"></div> | |
</div> | |
<div class="left-arm box" id="left-arm"> | |
<div class="arm"> | |
<div class="top skin"></div> | |
<div class="left skin"></div> | |
<div class="right skin"></div> | |
<div class="front skin"></div> | |
<div class="back skin"></div> | |
<div class="down skin"></div> | |
</div> | |
</div> | |
<div class="right-arm box" id="right-arm"> | |
<div class="arm"> | |
<div class="top skin"></div> | |
<div class="left skin"></div> | |
<div class="right skin"></div> | |
<div class="front skin"></div> | |
<div class="back skin"></div> | |
<div class="down skin"></div> | |
</div> | |
</div> | |
<div class="left-leg box" id="left-leg"> | |
<div class="leg"> | |
<div class="top skin"></div> | |
<div class="left skin"></div> | |
<div class="right skin"></div> | |
<div class="front skin"></div> | |
<div class="back skin"></div> | |
<div class="down skin"></div> | |
</div> | |
</div> | |
<div class="right-leg box" id="right-leg"> | |
<div class="leg"> | |
<div class="top skin"></div> | |
<div class="left skin"></div> | |
<div class="right skin"></div> | |
<div class="front skin"></div> | |
<div class="back skin"></div> | |
<div class="down skin"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="flash" id="flash"></div> | |
<div class="button-container" id="button-container"> | |
<button id="adjust" class="baton selector fas fa-arrows-alt" title="Adjust the model"></button> | |
<button id="rotate" class="baton selector fas fa-sync-alt" title="Rotate the model"></button> | |
<button id="scale" class="baton selector fas fa-expand" title="Scale the model"></button> | |
<button id="flip" class="baton fas fa-exchange-alt" title="Flip the model along X or Y axis"></button> | |
<button id="thumbnail" class="baton fas fa-camera-retro" title="Capture thumbnail"></button> | |
<button id="visible" class="baton fas fa-user" title="Hide/show the character model"></button> | |
<button id="reset" class="baton fas fa-undo-alt" title="Reset the view"></button> | |
</div> | |
<div class="helpcontainer"> | |
<div class="question" id="question"><p class="fas fa-question-circle"></i></div> | |
<div class="help" id="help"> | |
<p>Drag the scene to rotate the view</p> | |
<p>Drag the gun to adjust it</p> | |
<p>Click <span class="fas fa-camera-retro"></span> to take a thumbnail</p> | |
<p>Click <span class="fas fa-arrows-alt"></span>, <span class="fas fa-sync-alt"></span>, <span class="fas fa-expand"></span> to select adjustment mode</p> | |
<p>Click <span class="fas fa-exchange-alt"></span> to flip the gun</p> | |
<p>Zoom in/out with mouse wheel</p> | |
<p>Click <span class="fas fa-user"></span> / <span class="fas fa-user-slash"></span> to hide/show the character</p> | |
<p>Click <span class="fas fa-undo-alt"></span> to reset the view</p> | |
<p>Buttons show when you hover over the current mode button</p> | |
</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const yAngleLimit = 75, | |
scalePerTick = 0.002, | |
gunFlipXTransform = 'rotateX(180deg)', | |
gunFlipYTransform = 'rotateY(180deg)';//' translateX(-4.5em)'; | |
var rY = 0, | |
rX = 0, | |
scale = 1, | |
mouseOrigin = {}, | |
dragging = false, | |
gunMove = false, | |
gunRotate = false, | |
gunScaling = false, | |
diffX = 0, | |
diffY = 0, | |
dirX = {x:1, y:0, z:0}, | |
dirY = {x:0, y:1, z:0}, | |
gunFlipX = false, | |
gunFlipY = false, | |
nextFlip = 'x', | |
gunTranslateX = -1*64, | |
gunTranslateY = -2*64, | |
gunTranslateZ = 2*64, | |
gunRotateX = 0, | |
gunRotateY = 0, | |
gunRotateZ =0, | |
gunScale = 1, | |
darkened = false, | |
activeBaton = 'adjust'; | |
window.addEventListener('mousedown', (e) => { | |
if (document.querySelectorAll('#button-container:hover').length>0) { | |
// hovering over the buttons | |
return; | |
} else if (document.querySelectorAll('#gun:hover').length>0) { | |
// hovering over the gun | |
if (activeBaton === 'adjust') { | |
gunMove = true; | |
} else if (activeBaton === 'rotate') { | |
gunRotate = true; | |
} else if (activeBaton === 'scale') { | |
gunScaling = true; | |
} | |
} else if (document.querySelectorAll('#scene-container:hover').length>0) { | |
dragging = true; | |
} | |
e.preventDefault(); | |
mouseOrigin = {x:e.pageX, y:e.pageY}; | |
diffX=0; | |
diffY=0; | |
}); | |
function defineAxis() { | |
let alphaRad = rX/180*Math.PI, | |
betaRad = rY/180*Math.PI, | |
xD = { x: Math.cos(rX/180*Math.PI), y: Math.sin(rX/180*Math.PI) }, | |
yD = { x: Math.sin(rY/180*Math.PI), y: Math.cos(rY/180*Math.PI) }, | |
zD = { x: Math.sin(rX/180*Math.PI)*Math.cos(rY/180*Math.PI), y: -Math.cos(rX/180*Math.PI)*Math.sin(rY/180*Math.PI) }; | |
dirX = {x: xD.x, y: yD.x, z: zD.x}, | |
dirY = {x: xD.y, y: yD.y, z: zD.y}; | |
} | |
window.addEventListener('mouseup', (e) => { | |
if (dragging) { | |
e.preventDefault(); | |
rX+=diffX; | |
rY+=diffY; | |
if (rY<-yAngleLimit) rY = -yAngleLimit; | |
if (rY>yAngleLimit) rY = yAngleLimit; | |
while (rX > 360) { rX -= 360; } | |
while (rX < -360) { rX += 360; } | |
defineAxis(); | |
} | |
dragging = false; | |
gunMove = false; | |
gunRotate = false; | |
gunScaling = false; | |
diffX=0; | |
diffY=0; | |
}); | |
function sceneTransform() { | |
let posY = Math.min(yAngleLimit,Math.max(-yAngleLimit,rY+diffY)); | |
let scene = document.getElementById('scene'); | |
scene.style.transform=`rotateY(${rX+diffX}deg) rotateX(${posY}deg) scale3d(${scale}, ${scale}, ${scale})`; | |
} | |
function gunTransform() { | |
if (gunMove) { | |
gunTranslateX += diffX*dirX.x + diffY*dirY.x; | |
gunTranslateY += diffX*dirX.y + diffY*dirY.y; | |
gunTranslateZ += diffX*dirX.z + diffY*dirY.z; | |
diffX = 0; | |
diffY = 0; | |
} else if (gunScaling) { | |
let angle = Math.atan2(diffX,diffY)/Math.PI*180, | |
sign = (angle>-45 && angle<=135)?1:-1, | |
dist = Math.hypot(diffX,diffY) * sign; | |
gunScale = Math.max(1/16, Math.min(16, gunScale * Math.pow(1.1,dist))); | |
diffX = 0; | |
diffY = 0; | |
} else if (gunRotate) { | |
gunRotateX -= diffY; | |
gunRotateY += diffX; | |
diffX = 0; | |
diffY = 0; | |
} | |
let gun = document.getElementById('gun'); | |
let gunPivot = document.getElementById('gun-pivot'); | |
gunPivot.style.transform = `translate3d(${gunTranslateX}px, ${gunTranslateY}px, ${gunTranslateZ}px)`; | |
let transformString = `rotateX(${gunRotateX}deg) rotateY(${gunRotateY}deg) rotateZ(${gunRotateZ}deg) scale3d(${gunScale}, ${gunScale}, ${gunScale})`; | |
gun.style.transform = transformString; | |
} | |
window.addEventListener('mousemove', (e) => { | |
if (dragging || gunMove || gunScaling || gunRotate) { | |
e.preventDefault(); | |
diffX = e.pageX - mouseOrigin.x; | |
diffY = e.pageY - mouseOrigin.y; | |
} | |
if (dragging) { | |
sceneTransform(); | |
} else if (gunRotate || gunMove || gunScaling) { | |
gunTransform(); | |
mouseOrigin = {x: e.pageX, y: e.pageY}; | |
} | |
}); | |
window.addEventListener('wheel', e=> { | |
if (document.querySelectorAll('#scene-container:hover').length>0) { | |
scale = Math.min(10, Math.max(0.1, scale - e.deltaY * scalePerTick)); | |
sceneTransform(); | |
} | |
}); | |
document.querySelectorAll('.selector').forEach(s=>{s.addEventListener('click', e=>{ | |
document.getElementById(activeBaton).classList.remove('selected'); | |
e.target.classList.add('selected'); | |
activeBaton = e.target.id; | |
});}); | |
document.getElementById('thumbnail').addEventListener('click', e => { | |
let o = document.getElementById('flash'); | |
o.style.display = 'initial'; | |
o.style.opacity = 0.95; | |
document.getElementById('click-sound').play(); | |
setTimeout(() => { | |
o.style.opacity=0; | |
setTimeout(() => { | |
o.style.display = 'none'; | |
},100); | |
},100); | |
}); | |
document.getElementById('flip').addEventListener('click', e => { | |
if (nextFlip === 'x') { | |
gunFlipX = !gunFlipX; | |
nextFlip = 'y'; | |
} else { | |
nextFlip = 'x'; | |
gunFlipY = !gunFlipY; | |
} | |
document.getElementById('gun-flipper').style.transform = gunFlipX||gunFlipY?(gunFlipX?gunFlipXTransform:'') + ' ' + (gunFlipY?gunFlipYTransform:''):'initial'; | |
}); | |
document.getElementById('visible').addEventListener('click', e => { | |
let btn = e.target; | |
if (!btn.classList.contains('selected')) { | |
btn.classList.remove('fa-user'); | |
btn.classList.add('selected', 'fa-user-slash'); | |
document.getElementById('char').style.visibility = 'hidden'; | |
} else { | |
btn.classList.remove('selected', 'fa-user-slash'); | |
btn.classList.add('fa-user'); | |
document.getElementById('char').style.visibility = 'visible'; | |
} | |
}); | |
window.addEventListener('load', ()=>{ | |
document.getElementById(activeBaton).classList.add('selected'); | |
}); | |
document.getElementById('reset').addEventListener('click', ()=>{ | |
rX=0; | |
rY=0; | |
diffX=0; | |
diffY=0; | |
scale=1; | |
sceneTransform(); | |
defineAxis(); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap'); | |
body { | |
background-color: #34d0e9; | |
--tile-size: 64px; | |
height: 98vh; | |
width: 100vw; | |
margin: 0; | |
top:0; | |
left:0; | |
bottom:0; | |
right:0; | |
position: relative; | |
} | |
p { | |
font-family: 'Quicksand', sans-serif; | |
font-size: 16pt; | |
font-weight: 450; | |
color: darkslategray; | |
display: block; | |
background-color: whitesmoke; | |
border-radius:12px; | |
padding: 6px 10px 6px 10px; | |
margin: 6px; | |
} | |
.scene-container { | |
position: relative; | |
box-sizing: border-box; | |
border-radius: 12px; | |
border: 5px solid white; | |
margin: 16px 16px 0 16px; | |
width: calc(100vw - 32px); | |
height: calc(60vh - 64px); | |
font-size: var(--tile-size); | |
padding:0px; | |
display:flex; | |
justify-content:center; | |
align-items:center; | |
transform-style:preserve-3d; | |
perspective: 80em; | |
perspective-origin: center; | |
overflow:hidden; | |
background-color: #01ffff; | |
} | |
.draggable-scene { | |
height:100%; | |
width:100%; | |
position:relative; | |
margin-left: 50%; | |
margin-top:70vh; | |
transform-style: preserve-3d; | |
transform-origin: 0 0; | |
z-index:0; | |
} | |
.swirl { | |
animation: sceneRotate 10s infinite linear; | |
@keyframes sceneRotate { | |
to { transform: rotateY(360deg); } | |
} | |
} | |
.floor { | |
background-image: linear-gradient(to right, transparent 95%, aliceblue 95%), | |
linear-gradient(to bottom, cadetblue 95%, aliceblue 95%); | |
background-size: 1em 1em, 1em 1em; | |
width:16em; | |
height:16em; | |
opacity:0.3; | |
position:absolute; | |
top: 2em; | |
--translate-value: -50%, -50%; | |
--rotateX-value: 90deg; | |
transform: translate(-50%, -50%) | |
rotateX(90deg); | |
} | |
.skin { | |
position: absolute; | |
top:0; | |
left:0; | |
width:1em; | |
height:1em; | |
background-color:#f97; | |
box-shadow: inset 0 0 16px #c64; | |
} | |
.metal { | |
position: absolute; | |
top:0; | |
left:0; | |
width: 1em; | |
height: 1em; | |
background-color:#789; | |
background-image:radial-gradient(#0003 5%, #789 60%, #0002 62%, #FFF3 67%, #789 80%, #FFF5); | |
box-shadow: inset 0 0 16px #fffa; | |
} | |
.box { | |
transform-style: preserve-3d; | |
height: 1em; | |
width: 1em; | |
position: absolute; | |
top:0; | |
left:0; | |
.top { | |
transform: rotatex(90deg) translatez(0.5em); | |
} | |
.down { | |
transform: rotatex(-90deg) translatez(0.5em); | |
} | |
.left { | |
transform: rotatey(90deg) translatez(0.5em); | |
} | |
.right { | |
transform: rotatey(-90deg) translatez(0.5em); | |
} | |
.front { | |
transform: translatez(0.5em); | |
transform-style: preserve-3d; | |
} | |
.back { | |
transform: rotatey(180deg) translatez(0.5em); | |
} | |
} | |
.face { | |
position: absolute; | |
top:0; | |
left:0; | |
height: 1em; | |
width: 1em; | |
transform: translatez(0.1em); | |
background-image:url("data:image/svg+xml;utf8,<svg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' version='1.1'> <circle cx='8' cy='10' fill='slategray' r='5'/><circle cx='24' cy='10' fill='slategray' r='5'/></svg>"); | |
} | |
.head { | |
transform: scale3d(1.1, 1.1, 1.1) translate(-0.5em, -4.1em); | |
} | |
.body { | |
transform: scale3d(2, 3, 1.3) translate3d(-0.25em, -0.75em, 0); | |
} | |
.arm { | |
transform-style: preserve-3d; | |
transform: scale3d(0.75, 2.5, 0.75); | |
} | |
.leg { | |
transform-style: preserve-3d; | |
transform: scale3d(0.85, 2, 0.85); | |
} | |
.left-arm { | |
transform-origin: top center; | |
transform: translate(-1.9em, -3.2em) | |
rotateX(30deg) rotatez(10deg); | |
} | |
.right-arm { | |
transform-origin: top center; | |
transform: translate(0.9em, -3.2em) | |
rotateX(30deg) rotatez(-10deg); | |
} | |
.left-leg { | |
transform-origin: top center; | |
transform: translate(-1.2em, -0.1em) rotatez(10deg) skewy(-10deg); | |
} | |
.right-leg { | |
transform-origin: top center; | |
transform: translate(0.2em, -0.1em) rotatez(-10deg) skewy(10deg); | |
} | |
.gun { | |
position: absolute; | |
top:0; | |
left:0; | |
z-index:2; | |
transform-origin: center center center; | |
transform-style: preserve-3d; | |
} | |
.stock { | |
transform-style:preserve-3d; | |
transform: scale3d(2, 0.5, 0.5) translate3d(-0.25em, -2em, 0); | |
} | |
.handle { | |
transform-style:preserve-3d; | |
transform: scale3d(0.5, 1.2, 0.4) rotatez(25deg) skewy(-25deg) translate3d(-2.4em, -0.5em, 0); | |
} | |
.aim { | |
transform-style:preserve-3d; | |
transform: scale3d(0.2, 0.25, 0.2) translate3d(2em, -5em, 0); | |
} | |
.button-container { | |
position: absolute; | |
top: 10px; | |
left: 24px; | |
background-color:#34d0e9; | |
padding:8px; | |
display: flex; | |
flex-flow: column nowrap; | |
gap: 8px; | |
border-radius: 8px; | |
overflow: none; | |
transition: flex 1s ease-in-out; | |
} | |
.baton { | |
font-size:18pt; | |
color:darkslategray; | |
border-radius: 8px; | |
border: none; | |
padding:8px 12px; | |
width: 2.2em; | |
} | |
#button-container .baton { | |
display: none; | |
} | |
.baton:hover { | |
background-color:wheat; | |
} | |
.baton:active { | |
background-color: burlywood; | |
color: cornsilk; | |
} | |
.selected { | |
background-color: burlywood; | |
} | |
#button-container .baton.selected { | |
display: initial; | |
} | |
#button-container:hover .baton { | |
display: initial; | |
} | |
.gun-flipper { | |
position:absolute; | |
top:0; | |
left:0; | |
transform-style:preserve-3d; | |
} | |
.flash { | |
position:absolute; | |
width:100%; | |
height:100%; | |
top:0; | |
left:0; | |
background-color:white; | |
opacity:0; | |
transition: opacity 0.1s ease-out; | |
display: none; | |
} | |
.char { | |
transform-style: preserve-3d; | |
} | |
.gun-pivot { | |
position:absolute; | |
top:0; | |
left:0; | |
transform-origin: center center center; | |
transform-style: preserve-3d; | |
transform: translate3d(-1em, -2em, 2em); | |
} | |
#gun-pivot:hover + #char .skin { | |
background-color: #fdb; | |
box-shadow: inset 0 0 16px #ea8; | |
} | |
.helpcontainer { | |
display: flex; | |
flex-flow: row nowrap; | |
width: max(60%, 500px); | |
margin-left: min(140px, 12%); | |
} | |
.help { | |
display: none; | |
width: 100%; | |
} | |
#question:hover + #help { | |
display: initial; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A prototype of a UI for adjusting a model of a gun relative to the character model using only plain HTML, JavaScript, and CSS