Canvas Times Tables (Circles)
본문
https://codepen.io/EntropyReversed/pen/YBEwXV
HTML :
<div class="multiplier-wrap">
<div class="multiplier-plusTen">
<h4>add</h4>
<button class="controlBtn" data-value="10" id="plusTen">10</button>
<h4>to multiplier</h4>
</div>
<div class="multiplier">
<h4>multiplier</h4>
<button class="controlBtn" data-value="0" id="reset"><span id="mValue"></span></button>
<h4>click to reset</h4>
</div>
<div class="multiplier-plusOne">
<h4>add</h4>
<button class="controlBtn" data-value="1" id="plusOne">1</button>
<h4>to multiplier</h4>
</div>
</div>
<div class="main-wrap">
<div class="slideContainer slideContainer--speed">
<input
type="range"
id="speed"
class="slider"
name="speed"
min="0"
max="0.02"
value="0.008"
step="0.0001">
<h4 draggable="false">speed</h4>
</div>
<canvas class="graph"></canvas>
<div class="slideContainer slideContainer--points">
<input
type="range"
id="points"
class="slider"
name="points"
min="2"
max="200"
value="160"
step="1">
<h4 draggable="false">points</h4>
</div>
</div>
<h3>some cool patterns</h3>
<div class="coolPatterns-wrap">
<button onclick="patterns(21)" style="background-image:url(https://raw.githubusercontent.com/EntropyReversed/times-tables/master/21m.gif);">
</button>
<button onclick="patterns(51)" style="background-image:url(https://raw.githubusercontent.com/EntropyReversed/times-tables/master/51.gif);">
</button>
<button onclick="patterns(99)" style="background-image:url(https://raw.githubusercontent.com/EntropyReversed/times-tables/master/99m.gif);">
</button>
<button onclick="patterns(86)" style="background-image:url(https://raw.githubusercontent.com/EntropyReversed/times-tables/master/86m.gif);">
</button>
<button onclick="patterns(91)" style="background-image:url(https://raw.githubusercontent.com/EntropyReversed/times-tables/master/91m.gif);">
</button>
<button onclick="patterns(68)" style="background-image:url(https://raw.githubusercontent.com/EntropyReversed/times-tables/master/68m.gif);">
</button>
</div>
CSS :
@import url("https://fonts.googleapis.com/css?family=Major+Mono+Display");
:root {
--Hsl: 140;
}
* {
margin: 0;
padding: 0;
font-family: 'Major Mono Display', monospace;
box-sizing: border-box;
}
body {
background: #222;
text-align: center;
display: flex;
flex-direction: column;
min-height: 100vh;
padding-bottom: 40px;
}
.main-wrap {
display: flex;
align-items: center;
justify-content: space-around;
margin-bottom: 30px;
flex: 1;
touch-action: none;
}
canvas {
display: block;
max-width: 45vw;
height: auto;
max-height: 50vh;
width: auto;
}
h3,
h4 {
color: #fff;
}
h3 {
font-size: 14px;
margin-bottom: 10px;
}
h4 {
font-size: 10px;
}
.multiplier-wrap {
display: flex;
justify-content: space-around;
text-align: center;
margin: 15px 0 30px 0;
}
.multiplier-wrap button {
padding: 4px 16px;
width: 100px;
border-radius: 25px;
border-color: hsl(var(--Hsl), 72%, 78%);
color: #fff;
background-color: transparent;
outline: none;
}
.multiplier-wrap button:hover {
cursor: pointer;
}
.multiplier-wrap h4 {
margin-bottom: 4px;
}
.multiplier-wrap h4:last-of-type {
margin-bottom: 0;
margin-top: 4px;
}
.multiplier-wrap #mValue {
font-size: 16px;
}
.slideContainer {
position: relative;
display: block;
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
width: 200px;
line-height: 10px;
}
.slideContainer h4 {
display: flex;
flex-direction: row-reverse;
position: absolute;
bottom: 0;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
z-index: 1;
letter-spacing: 1px;
bottom: 24px;
left: 50%;
}
.slideContainer h4 span {
-webkit-transform-origin: center;
transform-origin: center;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.slideContainer h4 span:not(:first-of-type) {
margin-right: 10px;
}
.slideContainer--points h4 {
margin-bottom: 0;
bottom: -34px;
}
.slideContainer .slider {
position: relative;
z-index: 9999;
-webkit-appearance: none;
width: 100%;
height: 2px;
border-radius: 50%;
background: hsl(var(--Hsl), 72%, 78%);
outline: none;
opacity: .8;
}
.slideContainer .slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
background: hsl(var(--Hsl), 72%, 68%);
border: 2px solid #fff;
cursor: pointer;
opacity: 0.9;
}
.slideContainer .slider::-moz-range-thumb {
width: 25px;
height: 25px;
background: #4CAF50;
cursor: pointer;
}
.coolPatterns-wrap {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.coolPatterns-wrap button {
width: 80px;
height: 80px;
border-radius: 50%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border: none;
outline: none;
margin: 8px;
}
.coolPatterns-wrap button:hover {
cursor: pointer;
}
@media (max-width: 500px) {
.coolPatterns-wrap button {
width: 70px;
height: 70px;
}
}
Javascript :
const canvas = document.querySelector('.graph');
const ctx = canvas.getContext('2d');
const inputPoints = document.getElementById('points');
const inputSpeed = document.getElementById('speed');
const resetMultip = document.getElementById('reset');
const plusTen = document.getElementById('plusTen');
const plusOne = document.getElementById('plusOne');
const rangeLabels = document.querySelectorAll('.slideContainer h4');
rangeLabels.forEach((label) => {
const word = [...label.textContent];
label.innerHTML = '';
word.forEach((letter) => {
const spannedLetter = document.createElement('span');
spannedLetter.textContent = letter;
label.appendChild(spannedLetter);
});
});
function mValue(value) {
document.getElementById('mValue').textContent = value;
}
function dist(x1, y1, x2, y2) {
let xDist = x2 - x1;
let yDist = y2 - y1;
return Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2));
}
function map(value, minA, maxA, minB, maxB) {
return (1 - (value - minA) / (maxA - minA)) * minB + (value - minA) / (maxA - minA) * maxB;
}
let size = {
s: 8,
w: 60,
h: 60,
o: 1
};
size.sw = size.w * size.s;
size.sh = size.h * size.s;
size.mw = Math.floor(size.sw / 2);
size.mh = Math.floor(size.sh / 2);
size.lw = size.sw - size.s;
size.lh = size.sh - size.s;
canvas.width = size.sw;
canvas.height = size.sh;
let pi2 = Math.PI * 2;
let m = 1;
let drawGraph = function drawGraph() {
let root = document.documentElement;
let n = inputPoints.value;
if (m >= 999) {
m = 0;
} else {
m += +inputSpeed.value;
}
let r = Math.floor((size.w - 2 * size.o) * size.s * .5);
rect({ x: 0, y: 0, w: size.sw, h: size.sh }, '#222');
circle({ x: size.mw, y: size.mh, r: r }, '#333');
let x, y, e, ex, ey, start, end, lineL, a, h;
for (let i = 0; i < n; i++) {
x = Math.sin(pi2 / n * i - pi2 * .25) * r;
y = Math.sin(pi2 / n * i) * r;
e = m * i;
ex = Math.sin(pi2 / n * e - pi2 * .25) * r;
ey = Math.sin(pi2 / n * e) * r;
let Xw = size.mw + x;
let Yh = size.mh + y;
let eXw = size.mw + ex;
let eYh = size.mh + ey;
start = { x: Xw, y: Yh };
end = { x: eXw, y: eYh };
lineL = dist( Xw, Yh, eXw, eYh );
circle({ x: Xw, y: Yh, r: 2 }, '#fff');
a = map(lineL, 0, 400, 1, 0.25);
a = (a > 1 ? 1 : a).toFixed(2);
h = m * 100 % 360;
line(start, end, `hsla(${h}, 100%, 85%, ${a})`);
}
let parsM = parseFloat(Math.round(m * 100) / 100).toFixed(2);
mValue(parsM);
Hsl = ( + m * 100 % 360 );
root.style.setProperty('--Hsl', Hsl);
requestAnimationFrame(drawGraph);
};
let rect = function rect(r, color) {
ctx.fillStyle = color;
ctx.fillRect(r.x, r.y, r.w, r.h);
};
let line = function line(s, e, color) {
ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(s.x, s.y);
ctx.lineTo(e.x, e.y);
ctx.stroke();
};
let circle = function circle(c, color) {
ctx.strokeStyle = color;
ctx.beginPath();
ctx.arc(c.x, c.y, c.r, 0, pi2);
ctx.stroke();
};
function patterns(value) {
m = value;
inputSpeed.value = 0;
inputPoints.value = 200;
};
window.addEventListener('load', drawGraph);
document.querySelectorAll('.controlBtn').forEach(btn => {
btn.addEventListener('click', () => {
const btnValue = btn.getAttribute('data-value');
if (btnValue == 0) {
m = 0;
} else {
m += +btnValue;
}
});
});
- 이전글Random Password Generator 20.01.10
- 다음글마우스 이동에 따른 화려한 별빛 효과 20.01.10