Implemented text line height adjustment

This commit is contained in:
Igor Zinken
2020-12-30 13:25:32 +01:00
parent d34a990b88
commit 51e28701fb
8 changed files with 80 additions and 16 deletions

View File

@@ -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

View File

@@ -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() {

View File

@@ -3,6 +3,8 @@
"text": "Text",
"font": "Font",
"size": "Size",
"lineHeight": "Line height",
"spacing": "Letter spacing",
"color": "Color"
}
}

View File

@@ -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>

View File

@@ -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,
});
}

View File

@@ -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 );
});
};

View File

@@ -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;

View File

@@ -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"