Skip to content

Instantly share code, notes, and snippets.

@kerabromsmu
Created June 3, 2021 15:23
Show Gist options
  • Save kerabromsmu/3d90775547d67c159072815fe0f3cf2b to your computer and use it in GitHub Desktop.
Save kerabromsmu/3d90775547d67c159072815fe0f3cf2b to your computer and use it in GitHub Desktop.
Item adjust
<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>
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();
});
@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;
}
@kerabromsmu
Copy link
Author

A prototype of a UI for adjusting a model of a gun relative to the character model using only plain HTML, JavaScript, and CSS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment