분류 javascript

Canvas Times Tables (Circles)

컨텐츠 정보

  • 조회 273 (작성일 )

본문

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 : 


// https://www.webredone.com/

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;

    }

  });

});