Black friday Black friday
Aprílový black friday tě nenechá v klidu! Až 80 % prémiového obsahu zdarma. Více informací
Pouze tento týden slevy až 80 % na programování v Javě

Diskuze: Seven segment display - canvas

JavaScript JavaScript Seven segment display - canvas American English version English version

Aktivity (1)
Avatar
Petr Vopalecký:25.10.2018 21:05

Ahoj,
poradil by někdo jak optimálně naprogramovat vypisování jednotlivých číslic po vteřině z pole drawNumbers?

Zkusil jsem:

<!DOCTYPE html>
<html>
<head>
        <title></title>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
        <canvas id="myCanvas" width="180" height="300" style="border:1px solid #d3d3d3;">
                Your browser does not support the HTML5 canvas tag.
        </canvas>

        <script>
                $(document).ready(function(){
                        let log = console.info;
                        var x   = document.getElementById("myCanvas");
                        var ctx = x.getContext("2d");
                        ctx.fillStyle="grey";

                        var drawNumbers = [1,4,2,3,4];

                        function drawNumber(selectedNumber){
                                if (selectedNumber === 1){var number = ["b","c"];}
                                if (selectedNumber === 2){var number = ["a", "b", "g", "e", "d"];}
                                if (selectedNumber === 3){var number = ["a", "b", "g", "c", "d"];}
                                if (selectedNumber === 4){var number = ["b", "g", "c", "f"];}

                                //seven segments
                                if (number.includes("a")){ctx.fillRect(40, 20, 100, 20);} //a
                                if (number.includes("b")){ctx.fillRect(140, 40, 20, 100);}//b
                                if (number.includes("c")){ctx.fillRect(140, 160, 20, 100);}//c
                                if (number.includes("d")){ctx.fillRect(40, 260, 100, 20);}//d
                                if (number.includes("e")){ctx.fillRect(20, 160, 20, 100);}//e
                                if (number.includes("f")){ctx.fillRect(20, 40, 20, 100);}//f
                                if (number.includes("g")){ctx.fillRect(40, 140, 100, 20);}//g
                        }

                        function resetCanvas(){
                                ctx.clearRect(0, 0, x.width, x.height);
                        }

                        var arrayLength = drawNumbers.length;
                        for (var i = 0; i < arrayLength; i++) {
                            log(drawNumbers[i]);
                            drawNumber(drawNumbers[i]);
                        }

                });
        </script>
</body>
</html>

Chci docílit: Nedařím se mi přijít na to jak správně vložit timeout mezi vypisování jednotlivých číslic (a resetuCanvasu)

 
Odpovědět 25.10.2018 21:05
Avatar
Ondřej Šrytr:25.10.2018 22:56

Ahoj,

mohlo by to být takhle?

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <canvas id="myCanvas" width="180" height="300" style="border:1px solid #d3d3d3;">
        Your browser does not support the HTML5 canvas tag.
    </canvas>

    <script>
        window.addEventListener("load", function() {
            let log = console.info;
            var x   = document.getElementById("myCanvas");
            var ctx = x.getContext("2d");
            ctx.fillStyle="grey";

            var drawNumbers = [0,1,2,3,4,5,6,7,8,9];

            function drawNumber(selectedNumber){
                if (selectedNumber === 0){var number = ["a", "b", "c", "d", "e", "f"];}
                if (selectedNumber === 1){var number = ["b","c"];}
                if (selectedNumber === 2){var number = ["a", "b", "g", "e", "d"];}
                if (selectedNumber === 3){var number = ["a", "b", "g", "c", "d"];}
                if (selectedNumber === 4){var number = ["b", "g", "c", "f"];}
                if (selectedNumber === 5){var number = ["a", "c", "d", "f", "g"];}
                if (selectedNumber === 6){var number = ["a", "c", "d", "e", "f", "g"];}
                if (selectedNumber === 7){var number = ["a", "b", "c"];}
                if (selectedNumber === 8){var number = ["a", "b", "c", "d", "e", "f", "g"];}
                if (selectedNumber === 9){var number = ["a", "b", "c", "d", "f", "g"];}

                //seven segments
                if (number.includes("a")){ctx.fillStyle="red";ctx.fillRect(40, 20, 100, 20);} //a
                if (number.includes("b")){ctx.fillStyle="red";ctx.fillRect(140, 40, 20, 100);}//b
                if (number.includes("c")){ctx.fillStyle="red";ctx.fillRect(140, 160, 20, 100);}//c
                if (number.includes("d")){ctx.fillStyle="red";ctx.fillRect(40, 260, 100, 20);}//d
                if (number.includes("e")){ctx.fillStyle="red";ctx.fillRect(20, 160, 20, 100);}//e
                if (number.includes("f")){ctx.fillStyle="red";ctx.fillRect(20, 40, 20, 100);}//f
                if (number.includes("g")){ctx.fillStyle="red";ctx.fillRect(40, 140, 100, 20);}//g
            }

            //vynulovat
            function resetCanvas(){
                ctx.clearRect(0, 0, x.width, x.height);
                ctx.fillStyle="grey";
                ctx.fillRect(40, 20, 100, 20);
                ctx.fillRect(140, 40, 20, 100);
                ctx.fillRect(140, 160, 20, 100);
                ctx.fillRect(40, 260, 100, 20);
                ctx.fillRect(20, 160, 20, 100);
                ctx.fillRect(20, 40, 20, 100);
                ctx.fillRect(40, 140, 100, 20);
            }

            setTimeout(Timer, 1000);
            var aktualniindex = 0;
            function Timer() {
                resetCanvas(); //vynulovat
                drawNumber(drawNumbers[aktualniindex]); //vykreslit číslo
                if(aktualniindex < drawNumbers.length - 1) {
                    aktualniindex++;
                    setTimeout(Timer, 1000); //po provedení metody Timer() jí znovu za 1 sekundu zavoláme
                }
                else {
                    //jsme na konci, opakovat od nuly
                    aktualniindex = 0;
                    setTimeout(Timer, 1000);
                }
            }

        });
    </script>
</body>
</html>
 
Nahoru Odpovědět  +1 25.10.2018 22:56
Avatar
gold604
Člen
Avatar
gold604:25.10.2018 23:03

Pri animovaní sa používa funkcia window.requestAnimationFrame(), ktorá prijíma ako parameter callback. Tento callback sa zavolá pred následujúcim repaintom. Výhoda používať túto funkciu oproti napríklad window.setInterval() je ten, že to browser optimizuje(animácie prebehnú hladšie). Mal by si sa taktiež pozrieť na switch statement :)

Pozri si toto: https://codepen.io/…4/pen/YJdRpB. Prerobil som kompletne tvoj kód. Ak by si niečomu nechápal tak kľudne sa pýtaj. :)

For the lazy ones:

const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
context.fillStyle = "grey";

/*
* Seven Segment Display
*
*  __0__
* |     |
* 5     1
* | _6_ |
* |     |
* 4     2
* |__3__|
*
* n: [0, 1, 2, 3, 4, 5, 6]
*/
const digitSegments = {
  0: [true, true, true, true, true, true, false],
  1: [false, true, true, false, false, false, false],
  2: [true, true, false, true, true, false, true],
  3: [true, true, true, true, false, false, true],
  4: [false, true, true, false, false, true, true]
  /* TODO: finish rest of the digits */
}

/* Which digits to loop (and in what order)? */
let digitsToDraw = [0, 1, 2, 3, 4];
/* Delay between digits change (in seconds) */
let delayInSeconds = 1;

/* Draws digits on the canvas */
function drawDigit(digit) {
  if(digit > 9 || digit < 0) throw new RangeError('Not a digit.');

  digitSegments[digit].forEach((active, segment) => {
    if(!active) return;

    switch(segment){
      case 0:
          context.fillRect(40, 20, 100, 20);
          break;
      case 1:
          context.fillRect(140, 40, 20, 100);
          break;
      case 2:
          context.fillRect(140, 160, 20, 100);
          break;
      case 3:
          context.fillRect(40, 260, 100, 20);
          break;
      case 4:
          context.fillRect(20, 160, 20, 100);
          break;
      case 5:
          context.fillRect(20, 40, 20, 100);
          break;
      case 6:
          context.fillRect(40, 140, 100, 20);
          break;
    }
  });

}

/* Actual animating stuff */
let nextIndex = 0;
function animate() {
  drawDigit(digitsToDraw[nextIndex]);
  nextIndex = (nextIndex !== digitsToDraw.length -1) ? ++nextIndex : 0;
}


/* Animation Loop stuff */
let now, then;
function animationLoop() {
   now = Date.now();
   let elapsedInSeconds = (now - then) / 1000;

   if(elapsedInSeconds >= delayInSeconds || then === undefined){
     then = now;
     context.clearRect(0, 0, canvas.width, canvas.height);
     animate();
   }

  requestAnimationFrame(animationLoop);
}

requestAnimationFrame(animationLoop);
Editováno 25.10.2018 23:05
Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
 
Nahoru Odpovědět  +2 25.10.2018 23:03
Avatar
Odpovídá na Petr Vopalecký
Vladislav Ladicky:25.10.2018 23:13

Tak tak. Ako píše gold604. Po prvé, setInterval je príliš nespoľahlivý, používa sa requestAnimati­onFrame. Rovnako tie if sú nepatričné, prehľadnejšie je použiť switch. A už vôbec nevytvárať premenené v if. Vytvoriť premennú predtým a v if len priraďovať hodnoty. A ako posledné, necyklil by som to pole vo for, ale vyrobil prehľadnú generátor funkciu.

 
Nahoru Odpovědět 25.10.2018 23:13
Avatar
Odpovídá na gold604
Petr Vopalecký:25.10.2018 23:29

Paráda, díky. Mám co študýrovat.

 
Nahoru Odpovědět 25.10.2018 23:29
Avatar
Šimon Raichl
Překladatel
Avatar
Šimon Raichl:25.10.2018 23:33

Sice prichazim s krizkem po funuse, kazdopadne ale ten switch tam ani nemusi byt, muze to byt v jednom objektu a pak to proiterovat.

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
<canvas id="myCanvas" width="180" height="300" style="border:1px solid #d3d3d3;">
    Your browser does not support the HTML5 canvas tag.
</canvas>

<script>
    class Display {
        constructor() {
            this.canvas = document.getElementById("myCanvas");
            this.context = this.canvas.getContext("2d");
            this.context.fillStyle = "grey";
            this.drawNumbers = [0, 3, 1, 2, 3];
            this.numbers = [["b", "c"], ["a", "b", "g", "e", "d"], ["a", "b", "g", "c", "d"], ["b", "g", "c", "f"]];
            this.fill = {
                a: [40, 20, 100, 20],
                b: [140, 40, 20, 100],
                c: [140, 160, 20, 100],
                d: [40, 260, 100, 20],
                e: [20, 160, 20, 100],
                f: [20, 40, 20, 100],
                g: [40, 140, 100, 20]

            };
            this.run();
        }

        drawNumber(selectedNumber) {
            let numbers = this.numbers[selectedNumber];
            numbers.forEach((number) => {
                this.context.fillRect(...this.fill[number]);
            });
            this.resetCanvas();
        }

        resetCanvas(){
            this.context.clearRect(0, 0, this.context.width, this.context.height);
        }

        run(){
            let i = 0;
            let display = setInterval(() => {
                if (i < this.drawNumbers.length){
                    this.drawNumber(this.drawNumbers[i]);
                    i++;
                }
            }, 1000);
            i === this.drawNumbers.length ? clearInterval(display) : null;
        }
    }
    new Display();
</script>
</body>
</html>

Vim, interval bych nemel pouzit, ale na ukazku je to jednodussi.

 
Nahoru Odpovědět  +2 25.10.2018 23:33
Avatar
gold604
Člen
Avatar
Odpovídá na Šimon Raichl
gold604:26.10.2018 18:17

Na ten spread syntax som zabudol. Určite by som to radšej prerobil zo switch-u na spread syntax :)

 
Nahoru Odpovědět 26.10.2018 18:17
Avatar
Odpovídá na Petr Vopalecký
Vladislav Ladicky:27.10.2018 0:49

Konečne som mal čas, takže tu máš plne funkčný príklad:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Canvas</title>
  <style>
    canvas {
      border: 1px solid #d3d3d3;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="180" height="300">
    Your browser does not support the HTML5 canvas tag.
  </canvas>

  <script>
    'use strict'

    let canvas = document.querySelector('#myCanvas')
    let context = canvas.getContext('2d')

    let drawNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    let delay = 1 // in seconds

    let segments = {
      a: [40, 20, 100, 20],
      b: [140, 40, 20, 100],
      c: [140, 160, 20, 100],
      d: [40, 260, 100, 20],
      e: [20, 160, 20, 100],
      f: [20, 40, 20, 100],
      g: [40, 140, 100, 20]
    }
    let digits = [ // active segments
      [ // 0
        segments.a,
        segments.b,
        segments.c,
        segments.d,
        segments.e,
        segments.f
      ], [ // 1
        segments.b,
        segments.c
      ], [ // 2
        segments.a,
        segments.b,
        segments.d,
        segments.e,
        segments.g
      ], [ // 3
        segments.a,
        segments.b,
        segments.c,
        segments.d,
        segments.g
      ], [ // 4
        segments.b,
        segments.c,
        segments.f,
        segments.g
      ], [ // 5
        segments.a,
        segments.c,
        segments.d,
        segments.f,
        segments.g
      ], [ // 6
        segments.a,
        segments.c,
        segments.d,
        segments.e,
        segments.f,
        segments.g
      ], [ // 7
        segments.a,
        segments.b,
        segments.c
      ], [ // 8
        segments.a,
        segments.b,
        segments.c,
        segments.d,
        segments.e,
        segments.f,
        segments.g
      ], [ // 9
        segments.a,
        segments.b,
        segments.c,
        segments.d,
        segments.f,
        segments.g
      ]
    ]

    const animate = params => {
      let id = null
      let start = null

      const generate = generator(params.numbers)

      const repeatRequest = () => {
        id = window.requestAnimationFrame(step)
      }

      const stopRequesting = () => {
        window.cancelAnimationFrame(id)
      }

      const clearCanvas = () => {
        context.clearRect(0, 0, params.canvas.width, params.canvas.height)
      }

      const drawDigit = num => {
        params.digits[num].map(segments => {
          params.context.fillRect(...segments)
        })
      }

      const resetTimer = () => {
        start += 1000
        id = window.requestAnimationFrame(step)
      }

      const showNext = () => {
        let item = generate.next()

        item.done
          ? stopRequesting()
          : repaintCanvas(item.value)
      }

      const repaintCanvas = num => {
        clearCanvas()
        drawDigit(num)
        resetTimer()
      }

      const step = timestamp => {
        start = start || timestamp - delay * 1000

        timestamp - start >= delay * 1000
          ? showNext()
          : repeatRequest()
      }

      function* generator (arr) {
        for (let val of arr) yield val
      }

      id = window.requestAnimationFrame(step)
    }

    context.fillStyle = 'grey'
    window.addEventListener('load', animate.call(null, {
      numbers: drawNumbers,
      delay: delay,
      digits: digits,
      canvas: canvas,
      context: context
    }))
  </script>
</body>
</html>
 
Nahoru Odpovědět 27.10.2018 0:49
Avatar
Odpovídá na gold604
Vladislav Ladicky:27.10.2018 1:46

Tvoj spôsob, možno trochu vylepšený:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Canvas</title>
  <style>
    canvas {
      border: 1px solid #d3d3d3;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="180" height="300">
    Your browser does not support the HTML5 canvas tag.
  </canvas>

  <script>
    'use strict'

    let canvas = document.querySelector('#myCanvas')
    let context = canvas.getContext('2d')

    let drawNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    let delay = 1

    let segments = [
      [40, 20, 100, 20],
      [140, 40, 20, 100],
      [140, 160, 20, 100],
      [40, 260, 100, 20],
      [20, 160, 20, 100],
      [20, 40, 20, 100],
      [40, 140, 100, 20]
    ]
    let digits = [
      [true, true, true, true, true, true, false],
      [false, true, true, false, false, false, false],
      [true, true, false, true, true, false, true],
      [true, true, true, true, false, false, true],
      [false, true, true, false, false, true, true],
      [true, false, true, true, false, true, true],
      [true, false, true, true, true, true, true],
      [true, true, true, false, false, false, false],
      [true, true, true, true, true, true, true],
      [true, true, true, true, false, true, true]
    ]

    function animate (params) {
      let id = null
      let start = null
      let generate = generator(params.numbers)

      function repeatRequest () {
        id = window.requestAnimationFrame(step)
      }

      function stopRequesting () {
        window.cancelAnimationFrame(id)
      }

      function clearCanvas () {
        context.clearRect(
          0,
          0,
          params.canvas.width,
          params.canvas.height
        )
      }

      function drawDigit (num) {
        params.digits[num].forEach((isOn, idx) => {
          isOn && params.context.fillRect(...segments[idx])
        })
      }

      function resetTimer () {
        start += 1000
        id = window.requestAnimationFrame(step)
      }

      function showNext () {
        let item = generate.next()

        item.done
          ? stopRequesting()
          : repaintCanvas(item.value)
      }

      function repaintCanvas (num) {
        clearCanvas()
        drawDigit(num)
        resetTimer()
      }

      function step (timestamp) {
        start = start || timestamp - delay * 1000

        timestamp - start >= delay * 1000
          ? showNext()
          : repeatRequest()
      }

      function* generator (arr) {
        for (let val of arr) yield val
      }

      id = window.requestAnimationFrame(step)
    }

    context.fillStyle = 'grey'
    window.addEventListener('load', animate.call(null, {
      numbers: drawNumbers,
      delay: delay,
      segments: segments,
      digits: digits,
      canvas: canvas,
      context: context
    }))
  </script>
</body>
</html>
 
Nahoru Odpovědět 27.10.2018 1:46
Avatar
Vladislav Ladicky:27.10.2018 2:48

Funkčné, ešte trochu vylepšené demo

 
Nahoru Odpovědět 27.10.2018 2:48
Avatar
Vladislav Ladicky:27.10.2018 21:49

Vedel by niekto tento kód zjednodušiť? Mňa už fakt nič nenapadá...

<!DOCTYPE html>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Canvas</title>

<canvas id="myCanvas">
  Your browser does not support the HTML5 canvas tag.
</canvas>

<script>
  let drawNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  let delay = 1000

  let canvas = document.querySelector('#myCanvas')
  let context = canvas.getContext('2d')
  let scale = window.devicePixelRatio

  function animate (params) {
    let id = null
    let start = null

    let {numbers, delay, context} = params
    let generate = generator(numbers)

    let digits = '~0my3[_p{'
    let segments = [[2, 1, 5, 1],  [7, 2, 1, 5], [7, 8, 1, 5], [2, 13, 5, 1], [1, 8, 1, 5], [1, 2, 1, 5], [2, 7, 5, 1]]

    function* generator (arr) {
      for (let val of arr) yield val
    }

    function step (timestamp) {
      start = start || timestamp - delay

      if (timestamp - start >= delay) {
        let item = generate.next()

        if (!item.done) {
          context.clearRect(0, 0, context.canvas.width, context.canvas.height)

          Array.from(
            digits
              .charCodeAt(item.value)
              .toString(2)
              .padStart(7, '0'),
            (isOn, idx) => {
              Number(isOn)
                ? context.fillStyle = '#555'
                : context.fillStyle = '#eee'
              context.fillRect(...segments[idx].map(x => x * 20))
            })

          start += delay
          id = window.requestAnimationFrame(step)
        } else {
          window.cancelAnimationFrame(id)
        }
      } else {
        id = window.requestAnimationFrame(step)
      }
    }

    id = window.requestAnimationFrame(step)
  }

  canvas.width = canvas.width * scale
  canvas.height = canvas.height * scale
  context.transform(1, 0, -0.08, 1, 0, -0.08)

  window.addEventListener('load', animate.call(null, {
    numbers: drawNumbers,
    delay: delay,
    context: context
  }))
</script>

Live demo tu.

 
Nahoru Odpovědět 27.10.2018 21:49
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 11 zpráv z 11.