Знаете что это внезапная внезапность? Рандомайзер цвета в формате рулетки!
https://codepen.io/borntofrappe/pen/zyLoJR
[html]
<style>
/* import font(s) */
@import url("https://fonts.googleapis.com/css?family=Alegreya+Sans+SC:400,900");
/* detail root variables
--color-theme cascades to most html elements, and will be later updated in JavaScript
*/
:root {
--font: "Alegreya Sans SC", sans-serif;
--color-bg: #000b14;
--color-theme: #70a9fe;
}
/* heading centered atop the page */
h17 {
text-align: center;
margin: 1rem 0 0;
font-size: 3rem;
transition: all 0.25s ease-in-out;
display: block;
position: absolute;
right: 110px;
top: 160px;
border: 3px solid currentColor;
padding: 10px;
background: #fff;
color: var(--color-theme);
}
/* shown/hidden through an dedicated class */
h17.isHidden {
opacity: 0;
visibility: hidden;
transform: translateY(-1rem) scale(0);
}
/* wheel positioned to the left of the page, and occupying 50% of whichever dimension is the biggest */
svg#wheel {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 500px;
height: 500px;
}
/* arrow positioned to the right of the wheel, and pointing at the very middle section */
svg#pin {
position: absolute;
left: 500px;
width: calc(50vmax / 25);
height: calc(50vmax / 25);
top: 50%;
transform: translateY(-50%);
fill: var(--color-theme);
}
/* instructions displayed on the right side, in a single column layout */
.instructions {
min-height: 600px;
color: var(--color-theme);
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
padding: 1rem;
}
.instructions h2 {
font-size: 1rem;
letter-spacing: 0.1rem;
position: relative;
}
/* beside a silly exclamation point add a simple word in the middle of the sentence hinting at the innocent nature of the project */
.instructions h2:after {
content: "!";
}
.instructions h2:before {
position: absolute;
content: "suspicious";
font-size: 0.75rem;
opacity: 0.6;
bottom: 100%;
left: 50%;
transform: translateX(-50%) rotate(-12deg);
}
.instructions button {
margin-top: 1rem;
padding: 0.25rem 0.75rem;
font-size: 1.55rem;
font-family: inherit;
color: inherit;
border: none;
border-radius: 10px;
box-shadow: 0 0 0 2px currentColor;
background: var(--color-bg);
/* transition for a simple hover and active state */
transition: all 0.5s ease-out;
}
.instructions button:hover {
box-shadow: 0 0 0 1px var(--color-bg), 0 0 0 3px currentColor,
0 0 0 5px var(--color-bg), 0 0 0 7px currentColor;
}
.instructions button:active {
box-shadow: 0 0 0 2px currentColor, 0 0 0 4px var(--color-bg),
0 0 0 6px currentColor;
transform: scale(0.95) translateY(0.1rem);
}
/* cursor customized to grab, and not allowed when the wheel is spinning (class added in JS) */
.instructions button,
svg#pin {
cursor: grab;
}
.instructions button.isSpinning,
svg#pin.isSpinning {
cursor: not-allowed;
}
/*
animation for the pin, to have it soundly move up and down alongside the spinning wheel
the duration of the animation is customized in JS to have it run a certain number of times
*/
@keyframes pinWheel {
33% {
transform: translateY(-50%) rotate(-10deg);
}
67% {
transform: translateY(-50%) rotate(10deg);
}
}
</style>
<script>
// target the SVG and the pin right next to it
const containerSlices = document.querySelector('g#slices');
const pin = document.querySelector('svg#pin');
// immediately add simple dots around the wheel
for (let i = 0; i < 48; i += 1) {
const transform = `rotate(${360 / 48 * i}), translate(0 -49.5), rotate(${-360 / 48 * i})`;
const dot = `<circle r="0.5" cx="50" cy="50" fill="currentColor" transform="${transform}"/>`;
containerSlices.innerHTML += dot;
}
// target the heading and the button
const heading = document.querySelector('h17');
const spinButton = document.querySelector('button');
// variable updated for the timeout
let timeoutID = 0;
// utility functions returning a random integer in a range and a random hex value
const randomInt = (min = 0, max = 16) => Math.floor(Math.random() * (max - min) + min);
const randomHex = () => randomInt().toString(16);
// object used throughout the script, describing the colors and 3 particular rotation values
// the idea is to include the three slices aroud the wheel and have the arrow point always at one of them
const suspicious = [
{
rotation: 45,
color: 'A2CCB6'
},
{
rotation: 180,
color: 'FCEEB5'
},
{
rotation: 315,
color: 'EE786E'
}
];
// add a random fill color to the circle behind the slices
let randomFill = '';
for (let i = 0; i < 6; i += 1) {
randomFill += randomHex();
}
document.querySelector('svg circle#slice').style.fill = randomFill;
// create the slices, 24 in total, using a bit of trigonometry to compute the appropriate arc coordinates
for (let i = 360; i > 0; i -= 15) {
// values for the path element
const xCoor = 50 + Math.sin(i * Math.PI / 180) * 47;
const yCoor = 50 - Math.cos(i * Math.PI / 180) * 47;
const flags = i > 180 ? '0 1 1' : '0 0 1';
// initialize a variable for the fill color
let fill = '';
// create six random hex values for the fill color
// ! the look might be rather jarring
for (let j = 0; j < 6; j += 1) {
fill += randomHex();
}
// if the de-cremented variable matches the arbitrary rotation value of one of the objects, find the specific object
const suspect = suspicious.find(pairing => pairing.rotation === i);
// if existing, substitute the random hex with the value specified in said object
if (suspect) {
fill = suspect.color;
}
// create the path element and append it to the SVG container
const path = `
<path d="M 50 50 L 50 3 A 47 47 ${flags} ${xCoor} ${yCoor}" fill=#${fill} />
`;
containerSlices.innerHTML += path;
}
// function spinning the wheel
function spinWheel() {
// remove the event listener from the button and the wheel, to avoid running the function twice at the same time
spinButton.removeEventListener('click', spinWheel);
pin.removeEventListener('click', spinWheel);
// immediately hide the heading showing the matching color
heading.classList.add('isHidden');
// add a class to the pin and the button to show how they should not be clicked
pin.classList.add('isSpinning');
spinButton.classList.add('isSpinning');
// create variables for the duration of the rotation, as whell as the number of rotations achieved by the wheel
const randomDuration = randomInt(4, 10);
const randomRotate = randomInt(10, 20);
// crate a variable to pick from one of the objects at random
const randomSuspect = randomInt(0, 3);
// apply the transition and the transform properties
containerSlices.style.transformOrigin = '50%';
containerSlices.style.transition = `transform ${randomDuration}s ease-out`;
/* for the rotation, beside an arbitrary x360 rotation, remember to
- add 90 to match the position of the arrow (to the very right of the wheel)
- subtract the rotation of the slices
- add up to a slice as to have the arrow point within the slice's boundaries
*/
containerSlices.style.transform = `rotate(${randomRotate * 360 - suspicious[randomSuspect].rotation + 90 + randomInt(0, 360 / 24)}deg)`;
pin.style.animation = `pinWheel ${randomDuration / 10}s 10 ease-in-out`;
// after the time allocated for the rotation show the heading with the "random" color, update the custom property with its value
timeoutID = setTimeout(() => {
heading.textContent = `#${suspicious[randomSuspect].color}`;
heading.classList.remove('isHidden');
pin.style.animation = '';
document.documentElement.style.setProperty('--color-theme', `#${suspicious[randomSuspect].color}`);
// remove the class on the pin and button showing the forbidden cursor
pin.classList.remove('isSpinning');
spinButton.classList.remove('isSpinning');
// reset the event listener on the button and the pin
spinButton.addEventListener('click', spinWheel);
pin.addEventListener('click', spinWheel);
// clear the interval and set the boolean back to false
clearInterval(timeoutID);
}, randomDuration * 1000);
}
// attach a click event listener on the button, at which point call the spinWheel function
spinButton.addEventListener('click', spinWheel);
// call the same function when pressing the pin
pin.addEventListener('click', spinWheel);
</script>
<!--
project's structure
- svg for the color wheel (consisting of circle elements, and a group in which the slices are added)
- svg for the arrow pointing at the selected color (positioned to the right of the circle)
- heading in which to show the color selected through the wheel (hidden by default)
- container for the simple instructions and the button spinning the wheel
-->
<svg id="wheel" viewBox="0 0 100 100">
<circle
cx="50"
cy="50"
r="48"
fill="none"
stroke-width="1"
stroke="currentColor"
/>
<!-- add an id to the underlying circle as this depicts the final slice, and as to add a random color -->
<circle id="slice" cx="50" cy="50" r="47" fill="#001C34" />
<g id="slices"></g>
</svg>
<svg id="pin" viewBox="0 0 50 50"><path d="M 0 25 L 50 0 V 50" /></svg>
<h17 class="isHidden" style="user-select: all;">#ffffff00</h17>
<div class="instructions">
<h2>Spin that color <span>wheel</span></h2>
<button>Spin!</button>
</div>
[/html]











