Drone hologram outline
(c) 2023 All rights reserved
by PhilQ
HTML
<div id="drawing"></div>
<br>
<br>
<button onclick="document.getElementById('drawing')?.classList.toggle('spinning')">Play / Pause</button>
SCSS
* {
margin: 0;
padding: 0;
}
#drawing {
background: #fafafa;
width: 400px;
height: 400px;
margin: auto;
}
svg {
--rotor-color-1: rgba(204, 204, 204, 1);
--rotor-color-2: rgba(255, 255, 255, 0.5);
}
@keyframes spin-rotor {
0% { rotate: 0deg; }
100% { rotate: 360deg; }
}
.rotor-cw,
.rotor-ccw {
animation-name: spin-rotor;
animation-duration: 0.5s;
animation-iteration-count: infinite;
animation-direction: normal;
animation-fill-mode: forwards;
animation-timing-function: linear;
animation-play-state: paused;
}
.rotor-cw {
transform: rotate(45deg);
background: conic-gradient(
from 0turn at 50% 50%,
var(--rotor-color-2),
var(--rotor-color-1) 50%,
var(--rotor-color-2) 50%,
var(--rotor-color-1)
);
}
.rotor-ccw {
transform: rotate(-45deg);
animation-direction: reverse;
background: conic-gradient(
from 0turn at 50% 50%,
var(--rotor-color-1),
var(--rotor-color-2) 50%,
var(--rotor-color-1) 50%,
var(--rotor-color-2)
);
}
.guideline {
display: none;
fill: transparent;
stroke-width: 1;
stroke: #00f;
stroke-opacity: 1;
}
#drawing:hover .guideline {
display: initial;
}
#drawing.spinning .rotor-cw,
#drawing.spinning .rotor-ccw {
animation-play-state: running;
}
JavaScript
function make(tag, props = {}) {
let node = document.createElementNS('http://www.w3.org/2000/svg', tag);
for (let p in props) {
if (p === 'd') {
props[p] = props[p].join(' ');
}
node.setAttribute(p, props[p]);
}
return node;
}
function clone3x(node, parent) {
let clone;
for (let i = 1; i <= 3; i++) {
clone = node.cloneNode(true);
clone.setAttribute('transform', `rotate(${(i/4)*360})`);
clone.setAttribute('transform-origin', `50% 50%`);
parent.append(clone);
}
}
const c = {
size: 100, // half width
},
body = {
size: 0.45,
},
arm = {
end: {
offset: 0.9,
},
curve: {
depth: 0.06,
center: 0.5,
},
},
rotor = {
center: 0.5,
r: 0.33, //25,
ir: 0.075,
};
let tmp;
let svg = make('svg', {
width: '100%',
height: '100%',
viewBox: `0 0 ${2*c.size} ${2*c.size}`,
});
let guides = make('g');
// Guides - Diagonals
guides.append(make('line', {
'class': 'guideline',
'x1': 0,
'y1': 0,
'x2': 2*c.size,
'y2': 2*c.size,
}));
guides.append(make('line', {
'class': 'guideline',
'x1': 2*c.size,
'y1': 0,
'x2': 0,
'y2': 2*c.size,
}));
// Guides - Verticals
for (let i = 1; i <= 3; i++) {
guides.append(make('line', {
'class': 'guideline',
'x1': (i/4) * 2 * c.size,
'y1': 0,
'x2': (i/4) * 2 * c.size,
'y2': 2*c.size,
}));
}
// Guides - Horizontals
for (let i = 1; i <= 3; i++) {
guides.append(make('line', {
'class': 'guideline',
'y1': (i/4) * 2 * c.size,
'x1': 0,
'y2': (i/4) * 2 * c.size,
'x2': 2*c.size,
}));
}
// Guides - Inner square
guides.append(make('rect', {
'class': 'guideline',
'x': (1-body.size/2) * c.size,
'y': (1-body.size/2) * c.size,
'width': body.size * c.size,
'height': body.size * c.size,
}));
// Arms
let arm_coords = {
xs: (rotor.center + arm.end.offset * rotor.ir) * c.size,
xe: c.size,
ys: 0.5 * c.size,
ye: (1 - 0.5*body.size) * c.size,
};
let dir = 0.5 * Math.PI +...