diff --git a/receiverUI/EnvelopeGenerator.ReceiverUI.Web/EnvelopeGenerator.ReceiverUI.Web/wwwroot/js/error-space.js b/receiverUI/EnvelopeGenerator.ReceiverUI.Web/EnvelopeGenerator.ReceiverUI.Web/wwwroot/js/error-space.js new file mode 100644 index 00000000..80f1cae3 --- /dev/null +++ b/receiverUI/EnvelopeGenerator.ReceiverUI.Web/EnvelopeGenerator.ReceiverUI.Web/wwwroot/js/error-space.js @@ -0,0 +1,77 @@ +function drawVisor() { + const canvas = document.getElementById('visor'); + const ctx = canvas.getContext('2d'); + + ctx.beginPath(); + ctx.moveTo(5, 45); + ctx.bezierCurveTo(15, 64, 45, 64, 55, 45); + + ctx.lineTo(55, 20); + ctx.bezierCurveTo(55, 15, 50, 10, 45, 10); + + ctx.lineTo(15, 10); + + ctx.bezierCurveTo(15, 10, 5, 10, 5, 20); + ctx.lineTo(5, 45); + + ctx.fillStyle = '#2f3640'; + ctx.strokeStyle = '#f5f6fa'; + ctx.fill(); + ctx.stroke(); +} + +const cordCanvas = document.getElementById('cord'); +const ctx = cordCanvas.getContext('2d'); + +let y1 = 160; +let y2 = 100; +let y3 = 100; + +let y1Forward = true; +let y2Forward = false; +let y3Forward = true; + +function animate() { + requestAnimationFrame(animate); + ctx.clearRect(0, 0, innerWidth, innerHeight); + + ctx.beginPath(); + ctx.moveTo(130, 170); + ctx.bezierCurveTo(250, y1, 345, y2, 400, y3); + + ctx.strokeStyle = 'white'; + ctx.lineWidth = 8; + ctx.stroke(); + + + if (y1 === 100) { + y1Forward = true; + } + + if (y1 === 300) { + y1Forward = false; + } + + if (y2 === 100) { + y2Forward = true; + } + + if (y2 === 310) { + y2Forward = false; + } + + if (y3 === 100) { + y3Forward = true; + } + + if (y3 === 317) { + y3Forward = false; + } + + y1Forward ? y1 += 1 : y1 -= 1; + y2Forward ? y2 += 1 : y2 -= 1; + y3Forward ? y3 += 1 : y3 -= 1; +} + +drawVisor(); +animate(); \ No newline at end of file diff --git a/receiverUI/EnvelopeGenerator.ReceiverUI.Web/EnvelopeGenerator.ReceiverUI.Web/wwwroot/js/signature-pad.js b/receiverUI/EnvelopeGenerator.ReceiverUI.Web/EnvelopeGenerator.ReceiverUI.Web/wwwroot/js/signature-pad.js new file mode 100644 index 00000000..1833cfe7 --- /dev/null +++ b/receiverUI/EnvelopeGenerator.ReceiverUI.Web/EnvelopeGenerator.ReceiverUI.Web/wwwroot/js/signature-pad.js @@ -0,0 +1,114 @@ +// Minimal signature-pad implementation used by SignaturePadDialog.razor. +// Exposes window.signaturePad with attach() / clear() / toDataUrl() / detach(). +// +// Why a hand-rolled implementation instead of a library? +// • Zero external dependencies (no npm / no bundler). +// • Works identically under InteractiveServer and InteractiveWebAssembly +// because the Blazor side only calls four trivial JS functions. +// • Supports both mouse and pointer/touch events. +window.signaturePad = (function () { + const instances = new Map(); + + function resize(canvas) { + // Backing store size must follow the DPR so the line stays crisp. + const ratio = window.devicePixelRatio || 1; + const rect = canvas.getBoundingClientRect(); + canvas.width = Math.max(1, rect.width * ratio); + canvas.height = Math.max(1, rect.height * ratio); + const ctx = canvas.getContext('2d'); + ctx.scale(ratio, ratio); + ctx.lineWidth = 2; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + ctx.strokeStyle = '#0d2540'; + } + + function attach(canvasId) { + const canvas = document.getElementById(canvasId); + if (!canvas) return false; + if (instances.has(canvasId)) detach(canvasId); + + resize(canvas); + const ctx = canvas.getContext('2d'); + let drawing = false; + let hasInk = false; + let last = { x: 0, y: 0 }; + + function pos(ev) { + const rect = canvas.getBoundingClientRect(); + const clientX = ev.touches ? ev.touches[0].clientX : ev.clientX; + const clientY = ev.touches ? ev.touches[0].clientY : ev.clientY; + return { x: clientX - rect.left, y: clientY - rect.top }; + } + function start(ev) { + ev.preventDefault(); + drawing = true; + last = pos(ev); + } + function move(ev) { + if (!drawing) return; + ev.preventDefault(); + const p = pos(ev); + ctx.beginPath(); + ctx.moveTo(last.x, last.y); + ctx.lineTo(p.x, p.y); + ctx.stroke(); + last = p; + hasInk = true; + } + function end() { drawing = false; } + + const onResize = () => { + const data = canvas.toDataURL(); + resize(canvas); + const img = new Image(); + img.onload = () => ctx.drawImage(img, 0, 0, canvas.getBoundingClientRect().width, canvas.getBoundingClientRect().height); + img.src = data; + }; + + canvas.addEventListener('mousedown', start); + canvas.addEventListener('mousemove', move); + canvas.addEventListener('mouseup', end); + canvas.addEventListener('mouseleave', end); + canvas.addEventListener('touchstart', start, { passive: false }); + canvas.addEventListener('touchmove', move, { passive: false }); + canvas.addEventListener('touchend', end); + window.addEventListener('resize', onResize); + + instances.set(canvasId, { + canvas, ctx, + hasInk: () => hasInk, + clear: () => { ctx.clearRect(0, 0, canvas.width, canvas.height); hasInk = false; }, + toDataUrl: () => hasInk ? canvas.toDataURL('image/png') : null, + cleanup: () => { + canvas.removeEventListener('mousedown', start); + canvas.removeEventListener('mousemove', move); + canvas.removeEventListener('mouseup', end); + canvas.removeEventListener('mouseleave', end); + canvas.removeEventListener('touchstart', start); + canvas.removeEventListener('touchmove', move); + canvas.removeEventListener('touchend', end); + window.removeEventListener('resize', onResize); + } + }); + return true; + } + + function clear(canvasId) { + instances.get(canvasId)?.clear(); + } + + function toDataUrl(canvasId) { + return instances.get(canvasId)?.toDataUrl() ?? null; + } + + function detach(canvasId) { + const inst = instances.get(canvasId); + if (inst) { + inst.cleanup(); + instances.delete(canvasId); + } + } + + return { attach, clear, toDataUrl, detach }; +})();