mirror of
https://github.com/igorski/bitmappery.git
synced 2026-06-17 03:34:56 +02:00
Implemented text line height adjustment
This commit is contained in:
@@ -57,6 +57,7 @@ npm run lint
|
||||
# TODO / Roadmap
|
||||
|
||||
* Render service should use the tool-options-text debounce on first font load to ensure font is present on document load
|
||||
* Render service should measure total text bounding box upfront and scale resulting bitmap (can be done after render)
|
||||
* Try very wide Dropbox image on MB Air with space + pan in bottom right area. Won't work.
|
||||
* Restoring positions of documents with positioned rotated objects is not accurate
|
||||
* Issue with drawing mask on mirrored content
|
||||
|
||||
@@ -66,7 +66,7 @@ export default {
|
||||
}
|
||||
|
||||
.actions {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
@@ -79,6 +79,10 @@ export default {
|
||||
flex: 1;
|
||||
margin: $spacing-small;
|
||||
}
|
||||
|
||||
@include mobile() {
|
||||
position: fixed;
|
||||
}
|
||||
}
|
||||
|
||||
@include large() {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
"text": "Text",
|
||||
"font": "Font",
|
||||
"size": "Size",
|
||||
"lineHeight": "Line height",
|
||||
"spacing": "Letter spacing",
|
||||
"color": "Color"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,15 @@
|
||||
:tooltip="'none'"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'lineHeight'"></label>
|
||||
<slider
|
||||
v-model="lineHeight"
|
||||
:min="0"
|
||||
:max="172"
|
||||
:tooltip="'none'"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'color'"></label>
|
||||
<component
|
||||
@@ -79,6 +88,7 @@ export default {
|
||||
data: () => ({
|
||||
internalText: "",
|
||||
renderPending: false,
|
||||
layerId: null,
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters([
|
||||
@@ -108,7 +118,7 @@ export default {
|
||||
this.renderPending = false;
|
||||
this.updateLayer({
|
||||
index: this.activeLayerIndex,
|
||||
opts: { text: { value: this.text, size: this.size, font: this.font, color: this.color } },
|
||||
opts: this.formatOpts({ value })
|
||||
});
|
||||
}, 75 );
|
||||
}
|
||||
@@ -117,10 +127,21 @@ export default {
|
||||
get() {
|
||||
return this.activeLayer.text?.size;
|
||||
},
|
||||
set( value ) {
|
||||
set( size ) {
|
||||
this.updateLayer({
|
||||
index: this.activeLayerIndex,
|
||||
opts: { text: { value: this.text, size: value, font: this.font, color: this.color } },
|
||||
opts: this.formatOpts({ size })
|
||||
});
|
||||
}
|
||||
},
|
||||
lineHeight: {
|
||||
get() {
|
||||
return this.activeLayer.text?.lineHeight;
|
||||
},
|
||||
set( lineHeight ) {
|
||||
this.updateLayer({
|
||||
index: this.activeLayerIndex,
|
||||
opts: this.formatOpts({ lineHeight }),
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -128,10 +149,10 @@ export default {
|
||||
get() {
|
||||
return this.activeLayer.text?.color;
|
||||
},
|
||||
set( value ) {
|
||||
set( color ) {
|
||||
this.updateLayer({
|
||||
index: this.activeLayerIndex,
|
||||
opts: { text: { value: this.text, size: this.size, font: this.font, color: value } },
|
||||
opts: this.formatOpts({ color })
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -139,11 +160,11 @@ export default {
|
||||
get() {
|
||||
return this.activeLayer.text?.font;
|
||||
},
|
||||
async set( value ) {
|
||||
const fromCache = await loadGoogleFont( value );
|
||||
async set( font ) {
|
||||
const fromCache = await loadGoogleFont( font );
|
||||
this.updateLayer({
|
||||
index: this.activeLayerIndex,
|
||||
opts: { text: { value: this.text, size: this.size, font: value, color: this.color } },
|
||||
opts: this.formatOpts({ font })
|
||||
});
|
||||
// on first load, font is not immediately available for rendering
|
||||
if ( !fromCache ) {
|
||||
@@ -158,7 +179,10 @@ export default {
|
||||
activeLayer: {
|
||||
immediate: true,
|
||||
handler( layer ) {
|
||||
this.internalText = layer.text?.value;
|
||||
if ( this.layerId !== layer.id ) {
|
||||
this.internalText = layer.text?.value;
|
||||
this.layerId = layer.id;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -178,6 +202,18 @@ export default {
|
||||
handleBlur() {
|
||||
KeyboardService.setSuspended( false );
|
||||
},
|
||||
formatOpts( textOpts = {} ) {
|
||||
return {
|
||||
text: {
|
||||
value: this.text,
|
||||
size: this.size,
|
||||
lineHeight: this.lineHeight,
|
||||
font: this.font,
|
||||
color: this.color,
|
||||
...textOpts,
|
||||
}
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -23,9 +23,18 @@
|
||||
import { googleFonts } from "@/services/font-service";
|
||||
|
||||
const TextFactory = {
|
||||
create({ font = googleFonts[ 0 ], value = "", size = 16, color = "red" } = {}) {
|
||||
create({
|
||||
font = googleFonts[ 0 ],
|
||||
value = "",
|
||||
size = 16,
|
||||
lineHeight = 0,
|
||||
spacing = 0,
|
||||
color = "red"
|
||||
} = {}) {
|
||||
return {
|
||||
font,
|
||||
lineHeight,
|
||||
spacing,
|
||||
value,
|
||||
size,
|
||||
color,
|
||||
@@ -41,6 +50,8 @@ const TextFactory = {
|
||||
f: text.font,
|
||||
v: text.value,
|
||||
s: text.size,
|
||||
l: text.lineHeight,
|
||||
p: text.spacing,
|
||||
c: text.color,
|
||||
};
|
||||
},
|
||||
@@ -54,6 +65,8 @@ const TextFactory = {
|
||||
font: text.f,
|
||||
value: text.v,
|
||||
size: text.s,
|
||||
lineHeight: text.l,
|
||||
spacing: text.p,
|
||||
color: text.c,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -157,12 +157,12 @@ const renderText = async layer => {
|
||||
sourceCtx.fillStyle = text.color;
|
||||
|
||||
const lines = text.value.split( "\n" );
|
||||
const lineHeight = text.size;
|
||||
const lineHeight = text.lineHeight ? text.lineHeight : text.size;
|
||||
let y = 0;
|
||||
lines.forEach(( line, index ) => {
|
||||
const textMetrics = sourceCtx.measureText( line );
|
||||
y = lineHeight + index * lineHeight;
|
||||
y += Math.abs( textMetrics[ "actualBoundingBoxDescent" ]);
|
||||
//const textMetrics = sourceCtx.measureText( line );
|
||||
y = lineHeight + ( index * lineHeight );
|
||||
//y += Math.abs( textMetrics[ "actualBoundingBoxDescent" ]);
|
||||
sourceCtx.fillText( line, 0, y );
|
||||
});
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
@include large() {
|
||||
padding: $spacing-medium;
|
||||
height: calc(100% - $heading-height);
|
||||
height: calc(100% - #{$heading-height});
|
||||
}
|
||||
@include mobile() {
|
||||
padding: $spacing-xsmall $spacing-small;
|
||||
|
||||
@@ -7,6 +7,8 @@ describe( "Text factory", () => {
|
||||
const text = TextFactory.create();
|
||||
expect( text ).toEqual({
|
||||
size: expect.any( Number ),
|
||||
lineHeight: expect.any( Number ),
|
||||
spacing: 0,
|
||||
value: "",
|
||||
font: googleFonts[ 0 ],
|
||||
color: "red",
|
||||
@@ -16,12 +18,16 @@ describe( "Text factory", () => {
|
||||
it( "should be able to create a Text structure from given arguments", () => {
|
||||
const text = TextFactory.create({
|
||||
size: 10,
|
||||
lineHeight: 30,
|
||||
spacing: 50,
|
||||
font: "Helvetica",
|
||||
value: "Foo bar baz",
|
||||
color: "#FF00AE"
|
||||
});
|
||||
expect( text ).toEqual({
|
||||
size: 10,
|
||||
lineHeight: 30,
|
||||
spacing: 50,
|
||||
font: "Helvetica",
|
||||
value: "Foo bar baz",
|
||||
color: "#FF00AE"
|
||||
@@ -33,6 +39,8 @@ describe( "Text factory", () => {
|
||||
it( "should do so without data loss", async () => {
|
||||
const text = TextFactory.create({
|
||||
size: 10,
|
||||
lineHeight: 40,
|
||||
spacing: 10,
|
||||
font: "Helvetica",
|
||||
value: "Foo bar baz",
|
||||
color: "#FFF"
|
||||
|
||||
Reference in New Issue
Block a user