Diskuze: Seven segment display - canvas
V předchozím kvízu, Online test znalostí JavaScript, jsme si ověřili nabyté zkušenosti z kurzu.


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>
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);
+20 Zkušeností
+2,50 Kč

:25.10.2018 23:13
Tak tak. Ako píše gold604. Po prvé, setInterval je príliš nespoľahlivý, používa sa requestAnimationFrame. 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.
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.
: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
: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>
: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>
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.
Zobrazeno 11 zpráv z 11.