const animationTime = 100; let animationFinish = 0; class StackVisualizer { constructor(canvas, direction, blockSize, blockNum, margin) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); this.direction = direction; this.blockSize = blockSize; this.blockNum = blockNum; this.margin = margin; this.canvas.width = blockSize * blockNum + 2 * margin; this.canvas.height = blockSize + 2 * margin; this.elements = []; this.ctx.font = `${blockSize * 0.6}px monospace`; this.ctx.textAlign = "center"; this.ctx.textBaseline = "middle"; this.drawShape(); } drawBlock(x, y, text, opacity = 1.0) { this.ctx.lineWidth = 2; this.ctx.strokeStyle = `rgba(0, 0, 0, ${opacity})`; this.ctx.fillStyle = `rgba(127, 255, 127, ${opacity})`; this.ctx.beginPath(); this.ctx.moveTo(x, y); this.ctx.lineTo(x, y + this.blockSize); this.ctx.lineTo(x + this.blockSize, y + this.blockSize); this.ctx.lineTo(x + this.blockSize, y); this.ctx.closePath(); this.ctx.fill(); this.ctx.stroke(); this.ctx.fillStyle = `rgba(0, 0, 0, ${opacity})`; this.ctx.fillText(text, x + this.blockSize / 2, y + this.blockSize / 2); } drawShape(curElements = this.elements) { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); if (this.direction == 'right') { for (let j = 0; j < curElements.length; j++) { this.drawBlock(this.margin + j * this.blockSize, this.margin, curElements[j]); } this.ctx.lineWidth = 4; this.ctx.strokeStyle = `#000`; this.ctx.beginPath(); this.ctx.moveTo(this.margin + this.blockNum * this.blockSize, this.margin); this.ctx.lineTo(this.margin, this.margin); this.ctx.lineTo(this.margin, this.margin + this.blockSize); this.ctx.lineTo(this.margin + this.blockNum * this.blockSize, this.margin + this.blockSize); this.ctx.stroke(); } else if (this.direction == 'left') { for (let j = 0; j < curElements.length; j++) { this.drawBlock(this.margin + (this.blockNum - j - 1) * this.blockSize, this.margin, curElements[j]); } this.ctx.lineWidth = 4; this.ctx.strokeStyle = `#000`; this.ctx.beginPath(); this.ctx.moveTo(this.margin, this.margin); this.ctx.lineTo(this.margin + this.blockNum * this.blockSize, this.margin); this.ctx.lineTo(this.margin + this.blockNum * this.blockSize, this.margin + this.blockSize); this.ctx.lineTo(this.margin, this.margin + this.blockSize); this.ctx.stroke(); } } setPushAnimation(curElements, x) { setTimeout(() => { let start = Date.now(); let timer = setInterval(() => { let t = Date.now() - start; if (t >= animationTime) { clearInterval(timer); curElements.push(x); this.drawShape(curElements); return; } let completion = t / animationTime; this.drawShape(curElements); if (this.direction == 'right') { this.drawBlock(this.margin + (curElements.length + 1 - completion) * this.blockSize, this.margin, x, completion); } else if (this.direction == 'left') { this.drawBlock(this.margin + (this.blockNum - curElements.length - 1 - 1 + completion) * this.blockSize, this.margin, x, completion); } }, 20); }, Math.max(Date.now(), animationFinish) - Date.now()); } push(x) { if (this.elements.length >= this.blockNum) return; this.setPushAnimation([...this.elements], x); this.elements.push(x); } setPopAnimation(curElements, x) { setTimeout(() => { let start = Date.now(); let timer = setInterval(() => { let t = Date.now() - start; if (t >= animationTime) { clearInterval(timer); this.drawShape(curElements); return; } let completion = t / animationTime; this.drawShape(curElements); if (this.direction == 'right') { this.drawBlock(this.margin + (curElements.length + completion) * this.blockSize, this.margin, x, 1 - completion); } else if (this.direction == 'left') { this.drawBlock(this.margin + (this.blockNum - curElements.length - 1 - completion) * this.blockSize, this.margin, x, 1 - completion); } }, 20); }, Math.max(Date.now(), animationFinish) - Date.now()); } pop() { if (this.elements.length <= 0) return; let x = this.elements[this.elements.length - 1]; this.elements.pop(); this.setPopAnimation([...this.elements], x); } } window.onload = (e) => { let stack1 = new StackVisualizer(document.getElementById("stack1"), "left", 70, 7, 5); let stack2 = new StackVisualizer(document.getElementById("stack2"), "right", 70, 7, 5); const button_push = document.getElementById("button-push"); const button_pop = document.getElementById("button-pop"); let i = 1; function push() { if (Date.now() > animationFinish) { stack1.push(i); animationFinish = Math.max(Date.now(), animationFinish) + animationTime * 1.5; i++; } } function pop() { if (Date.now() > animationFinish) { if (stack2.elements.length > 0) { stack2.pop(); animationFinish = Math.max(Date.now(), animationFinish) + animationTime * 1.5; } else { while (stack1.elements.length != 0) { let x = stack1.elements[stack1.elements.length - 1]; stack1.pop(); stack2.push(x); animationFinish = Math.max(Date.now(), animationFinish) + animationTime * 1.5; } stack2.pop(); animationFinish = Math.max(Date.now(), animationFinish) + animationTime * 1.5; } } } button_push.onclick = push; button_pop.onclick = pop; };