stash
This commit is contained in:
parent
67c7415c3b
commit
ce1e5f1d62
1 changed files with 173 additions and 7 deletions
|
|
@ -95,8 +95,33 @@
|
||||||
Connect a printer to preview and print a label.
|
Connect a printer to preview and print a label.
|
||||||
</p>
|
</p>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="label-preview mb-3" v-show="selectedContent">
|
<div class="preview-row mb-3" v-show="selectedContent">
|
||||||
<canvas ref="labelCanvas"></canvas>
|
<div class="ruler-v">
|
||||||
|
<div class="ruler-v-corner"></div>
|
||||||
|
<div class="ruler ruler-v-ticks"
|
||||||
|
:style="{height: (tape.printAreaPx * zoom) + 'px'}">
|
||||||
|
<span v-for="t in verticalRulerTicks" :key="'tick-' + t.mm"
|
||||||
|
class="tick" :class="{'tick-major': t.major}"
|
||||||
|
:style="{top: t.pos + 'px'}"></span>
|
||||||
|
<span v-for="t in verticalRulerTicks.filter(t => t.major)"
|
||||||
|
:key="'label-' + t.mm" class="tick-label"
|
||||||
|
:style="{top: t.pos + 'px'}">{{ t.mm }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="preview-scroll">
|
||||||
|
<div class="ruler ruler-h"
|
||||||
|
:style="{width: (printedWidthPx * zoom) + 'px'}">
|
||||||
|
<span v-for="t in horizontalRulerTicks" :key="'tick-' + t.mm"
|
||||||
|
class="tick" :class="{'tick-major': t.major}"
|
||||||
|
:style="{left: t.pos + 'px'}"></span>
|
||||||
|
<span v-for="t in horizontalRulerTicks.filter(t => t.major)"
|
||||||
|
:key="'label-' + t.mm" class="tick-label"
|
||||||
|
:style="{left: t.pos + 'px'}">{{ t.mm }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="label-preview">
|
||||||
|
<canvas ref="labelCanvas"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-2 align-items-end">
|
<div class="row g-2 align-items-end">
|
||||||
|
|
@ -181,6 +206,9 @@ import {LABEL_TEMPLATES, BASE_VARS, DERIVED_VARS, withDerivedVars, templateConte
|
||||||
const BLOB_URL = "/vendor/libweblabel.js";
|
const BLOB_URL = "/vendor/libweblabel.js";
|
||||||
|
|
||||||
const MAX_ZOOM = 4; /* never magnify the preview more than this */
|
const MAX_ZOOM = 4; /* never magnify the preview more than this */
|
||||||
|
const MAX_PREVIEW_HEIGHT_PX = 300; /* never let the on-screen preview grow taller than this */
|
||||||
|
const RULER_TICK_MM = 1; /* finest tick spacing the mm ruler draws */
|
||||||
|
const RULER_MAJOR_EVERY_MM = 5; /* every Nth tick is taller and labeled with its mm value */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Print",
|
name: "Print",
|
||||||
|
|
@ -210,6 +238,12 @@ export default {
|
||||||
connected: null,
|
connected: null,
|
||||||
tape: null,
|
tape: null,
|
||||||
labelBitmap: null,
|
labelBitmap: null,
|
||||||
|
// The tape-fed preview's current on-screen scale and printed pixel width (see
|
||||||
|
// fitZoom/redraw) - tracked reactively, rather than read straight off the canvas
|
||||||
|
// element, purely so the mm ruler below can recompute its tick positions whenever
|
||||||
|
// either one changes.
|
||||||
|
zoom: 1,
|
||||||
|
printedWidthPx: 0,
|
||||||
|
|
||||||
// One input per *base* template variable (see label-layouts.js's BASE_VARS) - the
|
// One input per *base* template variable (see label-layouts.js's BASE_VARS) - the
|
||||||
// derived ones (itemUrl, itemHandle) are format strings calculated from these, not
|
// derived ones (itemUrl, itemHandle) are format strings calculated from these, not
|
||||||
|
|
@ -286,6 +320,25 @@ export default {
|
||||||
canPrint() {
|
canPrint() {
|
||||||
return Boolean(this.tape && this.labelBitmap && !this.busy);
|
return Boolean(this.tape && this.labelBitmap && !this.busy);
|
||||||
},
|
},
|
||||||
|
// On-screen pixels per real millimeter of tape, at the preview's current zoom - what
|
||||||
|
// turns a physical mm into a tick position the ruler can actually draw. Only meaningful
|
||||||
|
// for the tape-fed preview (see redraw's printedWidthPx) - the no-webusb fallback preview
|
||||||
|
// isn't fed from any particular real tape/dpi, so it gets no ruler (see the template).
|
||||||
|
tapePxPerMm() {
|
||||||
|
return this.tape ? (this.tape.dpi / 25.4) * this.zoom : 0;
|
||||||
|
},
|
||||||
|
// Ticks along the tape's length (the printed bitmap's actual width, lead/trailing feed
|
||||||
|
// margin included, since that's real physical tape too).
|
||||||
|
horizontalRulerTicks() {
|
||||||
|
if (!this.tape || !this.printedWidthPx) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return this.rulerTicks(this.printedWidthPx / (this.tape.dpi / 25.4));
|
||||||
|
},
|
||||||
|
// Ticks across the tape's fixed physical width.
|
||||||
|
verticalRulerTicks() {
|
||||||
|
return this.tape ? this.rulerTicks(this.tape.mediaWidthMm) : [];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
varValues: {
|
varValues: {
|
||||||
|
|
@ -324,6 +377,16 @@ export default {
|
||||||
return v.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, c => c.toUpperCase());
|
return v.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, c => c.toUpperCase());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// One tick per RULER_TICK_MM from 0 up to totalMm, each positioned in on-screen pixels
|
||||||
|
// via tapePxPerMm - shared by the horizontal/vertical ruler computeds above.
|
||||||
|
rulerTicks(totalMm) {
|
||||||
|
const ticks = [];
|
||||||
|
for (let mm = 0; mm <= totalMm; mm += RULER_TICK_MM) {
|
||||||
|
ticks.push({mm, pos: mm * this.tapePxPerMm, major: mm % RULER_MAJOR_EVERY_MM === 0});
|
||||||
|
}
|
||||||
|
return ticks;
|
||||||
|
},
|
||||||
|
|
||||||
async guard(fn) {
|
async guard(fn) {
|
||||||
this.error = null;
|
this.error = null;
|
||||||
this.busy = true;
|
this.busy = true;
|
||||||
|
|
@ -410,7 +473,8 @@ export default {
|
||||||
const bitmap = canvasToBitmap(canvas);
|
const bitmap = canvasToBitmap(canvas);
|
||||||
bitmapToCanvas(canvas, bitmap);
|
bitmapToCanvas(canvas, bitmap);
|
||||||
this.labelBitmap = bitmap;
|
this.labelBitmap = bitmap;
|
||||||
this.fitZoom(canvas);
|
this.printedWidthPx = canvas.width;
|
||||||
|
this.zoom = this.fitZoom(canvas);
|
||||||
},
|
},
|
||||||
|
|
||||||
redrawFallback() {
|
redrawFallback() {
|
||||||
|
|
@ -448,16 +512,20 @@ export default {
|
||||||
|
|
||||||
/* Fit the preview to its card without ever needing a horizontal
|
/* Fit the preview to its card without ever needing a horizontal
|
||||||
scrollbar for a label this small, magnifying short labels up to
|
scrollbar for a label this small, magnifying short labels up to
|
||||||
MAX_ZOOM rather than showing them at native (tiny) size. */
|
MAX_ZOOM rather than showing them at native (tiny) size - and never
|
||||||
|
past MAX_PREVIEW_HEIGHT_PX tall, however long/wide the label itself
|
||||||
|
runs. Returns the zoom actually used, so callers that care (see
|
||||||
|
redraw's ruler bookkeeping) don't have to re-derive it. */
|
||||||
fitZoom(canvas) {
|
fitZoom(canvas) {
|
||||||
const available = canvas.parentElement.clientWidth;
|
const available = canvas.parentElement.clientWidth;
|
||||||
if (!(available > 0)) {
|
if (!(available > 0)) {
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
const zoom = Math.min(MAX_ZOOM, available / canvas.width);
|
const zoom = Math.min(MAX_ZOOM, available / canvas.width, MAX_PREVIEW_HEIGHT_PX / canvas.height);
|
||||||
canvas.style.width = `${canvas.width * zoom}px`;
|
canvas.style.width = `${canvas.width * zoom}px`;
|
||||||
canvas.style.height = `${canvas.height * zoom}px`;
|
canvas.style.height = `${canvas.height * zoom}px`;
|
||||||
canvas.style.imageRendering = zoom >= 1 ? "pixelated" : "auto";
|
canvas.style.imageRendering = zoom >= 1 ? "pixelated" : "auto";
|
||||||
|
return zoom;
|
||||||
},
|
},
|
||||||
|
|
||||||
print() {
|
print() {
|
||||||
|
|
@ -476,7 +544,7 @@ export default {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const canvas = entry.target.querySelector("canvas");
|
const canvas = entry.target.querySelector("canvas");
|
||||||
if (canvas) {
|
if (canvas) {
|
||||||
this.fitZoom(canvas);
|
this.zoom = this.fitZoom(canvas);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
@ -528,6 +596,7 @@ export default {
|
||||||
background: rgba(127, 127, 127, .08);
|
background: rgba(127, 127, 127, .08);
|
||||||
border-radius: .35rem;
|
border-radius: .35rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-preview canvas {
|
.label-preview canvas {
|
||||||
|
|
@ -536,6 +605,103 @@ export default {
|
||||||
box-shadow: 0 0 0 1px rgba(127, 127, 127, .5);
|
box-shadow: 0 0 0 1px rgba(127, 127, 127, .5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The tape-fed preview's mm ruler (see Print.vue's template/script) - a horizontal track above
|
||||||
|
the canvas and a vertical one to its left, both ticked in real physical millimeters rather than
|
||||||
|
preview pixels, since what they're measuring is the actual label. */
|
||||||
|
.preview-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The single horizontal scroll region for a label wider than its card - the horizontal ruler and
|
||||||
|
the canvas are both direct children of this, so they scroll in lockstep with no JS needed to
|
||||||
|
keep them in sync. */
|
||||||
|
.preview-scroll {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overrides the standalone rule above: nested here, .label-preview must neither clip nor center
|
||||||
|
its canvas - overflow-x:visible lets the canvas's true width "bubble up" to .preview-scroll's
|
||||||
|
own scrollbar instead of scrolling twice, and text-align:left keeps the canvas flush with the
|
||||||
|
ruler's zero tick instead of drifting to the middle of whatever spare width this card has. */
|
||||||
|
.preview-scroll .label-preview {
|
||||||
|
overflow-x: visible;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruler-v {
|
||||||
|
flex: none;
|
||||||
|
width: 2.2rem;
|
||||||
|
color: rgba(127, 127, 127, .9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Matches .ruler-h's own height below - the vertical ruler's ticks start only after this, so tick
|
||||||
|
0 lines up with the canvas's top edge rather than the horizontal ruler sitting above it. */
|
||||||
|
.ruler-v-corner {
|
||||||
|
height: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruler-v-ticks {
|
||||||
|
position: relative;
|
||||||
|
margin-top: .75rem; /* .label-preview's own padding, so tick 0 meets the canvas, not its box */
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruler-h {
|
||||||
|
position: relative;
|
||||||
|
height: 1.6rem;
|
||||||
|
margin-left: .75rem; /* .label-preview's own padding, so tick 0 meets the canvas, not its box */
|
||||||
|
color: rgba(127, 127, 127, .9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruler .tick {
|
||||||
|
position: absolute;
|
||||||
|
background: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ticks anchor to the edge nearest the canvas (right for the vertical ruler, bottom for the
|
||||||
|
horizontal one) and grow outward from it, so they read as pointing at the label; the mm labels
|
||||||
|
sit on the opposite, outer edge, out of the ticks' way. */
|
||||||
|
.ruler-v-ticks .tick {
|
||||||
|
right: 0;
|
||||||
|
width: .4rem;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruler-v-ticks .tick-major {
|
||||||
|
width: .7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruler-h .tick {
|
||||||
|
bottom: 0;
|
||||||
|
width: 1px;
|
||||||
|
height: .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruler-h .tick-major {
|
||||||
|
height: .7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tick-label {
|
||||||
|
position: absolute;
|
||||||
|
font-size: .6rem;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* transform, not a fixed em nudge, so the label's actual center - not its edge - lands on the
|
||||||
|
tick's mm position (t.pos, set inline), whatever the text's width/height happens to be. */
|
||||||
|
.ruler-v-ticks .tick-label {
|
||||||
|
left: 0;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruler-h .tick-label {
|
||||||
|
top: 0;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
.copies-input {
|
.copies-input {
|
||||||
width: 5.5rem;
|
width: 5.5rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue