mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-05-17 18:11:08 +00:00
More text enhancements and fixes. Demo page now has italic/underline/overline/line-through and shadow buttons for text manipulations.
This commit is contained in:
parent
b60edcc379
commit
91205b4bb6
6 changed files with 194 additions and 84 deletions
54
dist/all.js
vendored
54
dist/all.js
vendored
|
|
@ -1192,14 +1192,7 @@ Cufon.registerEngine('canvas', (function() {
|
|||
|
||||
g.fillStyle = Cufon.textOptions.color || style.get('color');
|
||||
|
||||
if (textDecoration.underline) line(-font.face['underline-position'], g.fillStyle);
|
||||
if (textDecoration.overline) line(font.ascent, g.fillStyle);
|
||||
|
||||
function renderText() {
|
||||
if (isItalic) {
|
||||
g.save();
|
||||
g.transform(1, 0, -0.25, 1, 0, 0);
|
||||
}
|
||||
var left = 0;
|
||||
for (var i = 0, l = chars.length; i < l; ++i) {
|
||||
if (chars[i] === '\n') {
|
||||
|
|
@ -1208,20 +1201,50 @@ Cufon.registerEngine('canvas', (function() {
|
|||
continue;
|
||||
}
|
||||
var glyph = font.glyphs[chars[i]] || font.missingGlyph;
|
||||
|
||||
if (!glyph) continue;
|
||||
|
||||
var charWidth = Number(glyph.w || font.w) + letterSpacing;
|
||||
|
||||
if (textDecoration) {
|
||||
g.save();
|
||||
g.strokeStyle = g.fillStyle;
|
||||
g.beginPath();
|
||||
if (textDecoration.underline) {
|
||||
g.moveTo(0, -font.face['underline-position']);
|
||||
g.lineTo(charWidth, -font.face['underline-position']);
|
||||
}
|
||||
if (textDecoration.overline) {
|
||||
g.moveTo(0, font.ascent);
|
||||
g.lineTo(charWidth, font.ascent);
|
||||
}
|
||||
if (textDecoration['line-through']) {
|
||||
g.moveTo(0, -font.descent);
|
||||
g.lineTo(charWidth, -font.descent);
|
||||
}
|
||||
g.stroke();
|
||||
g.restore();
|
||||
}
|
||||
|
||||
if (isItalic) {
|
||||
g.save();
|
||||
g.transform(1, 0, -0.25, 1, 0, 0);
|
||||
}
|
||||
|
||||
g.beginPath();
|
||||
if (glyph.d) {
|
||||
if (glyph.code) interpret(glyph.code, g);
|
||||
else glyph.code = generateFromVML('m' + glyph.d, g);
|
||||
}
|
||||
g.fill();
|
||||
var charWidth = Number(glyph.w || font.w) + letterSpacing;
|
||||
|
||||
if (isItalic) {
|
||||
g.restore();
|
||||
}
|
||||
|
||||
g.translate(charWidth, 0);
|
||||
left += charWidth;
|
||||
}
|
||||
if (isItalic) {
|
||||
g.restore();
|
||||
}
|
||||
}
|
||||
|
||||
if (shadows) {
|
||||
|
|
@ -1238,11 +1261,6 @@ Cufon.registerEngine('canvas', (function() {
|
|||
g.save();
|
||||
renderText();
|
||||
g.restore();
|
||||
|
||||
if (textDecoration['line-through']) {
|
||||
line(-font.descent, g.fillStyle);
|
||||
}
|
||||
|
||||
g.restore();
|
||||
g.restore();
|
||||
|
||||
|
|
@ -9492,7 +9510,8 @@ fabric.util.animate = animate;
|
|||
fontFamily: this.fontfamily,
|
||||
enableTextDecoration: true,
|
||||
textDecoration: this.textDecoration,
|
||||
textShadow: this.textShadow
|
||||
textShadow: this.textShadow,
|
||||
fontStyle: this.fontStyle
|
||||
});
|
||||
|
||||
this.width = o.width;
|
||||
|
|
@ -9514,7 +9533,6 @@ fabric.util.animate = animate;
|
|||
|
||||
el.style.fontSize = '40px';
|
||||
el.style.fontWeight = '400';
|
||||
el.style.fontStyle = 'normal';
|
||||
el.style.letterSpacing = 'normal';
|
||||
el.style.color = '#000000';
|
||||
el.style.fontWeight = '600';
|
||||
|
|
|
|||
134
lib/cufon.js
134
lib/cufon.js
|
|
@ -644,16 +644,16 @@ Cufon.registerEngine('canvas', (function() {
|
|||
}
|
||||
|
||||
return function(font, text, style, options, node, el) {
|
||||
|
||||
|
||||
var redraw = (text === null);
|
||||
|
||||
|
||||
var viewBox = font.viewBox;
|
||||
|
||||
|
||||
var size = style.getSize('fontSize', font.baseSize);
|
||||
|
||||
|
||||
var letterSpacing = style.get('letterSpacing');
|
||||
letterSpacing = (letterSpacing == 'normal') ? 0 : size.convertFrom(parseInt(letterSpacing, 10));
|
||||
|
||||
|
||||
var expandTop = 0, expandRight = 0, expandBottom = 0, expandLeft = 0;
|
||||
var shadows = options.textShadow, shadowOffsets = [];
|
||||
if (shadows) {
|
||||
|
|
@ -668,11 +668,11 @@ Cufon.registerEngine('canvas', (function() {
|
|||
if (x < expandLeft) expandLeft = x;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var chars = Cufon.CSS.textTransform(redraw ? node.alt : text, style).split('');
|
||||
|
||||
|
||||
var width = 0, lastWidth = null;
|
||||
|
||||
|
||||
var maxWidth = 0, lines = 1;
|
||||
for (var i = 0, l = chars.length; i < l; ++i) {
|
||||
if (chars[i] === '\n') {
|
||||
|
|
@ -688,14 +688,14 @@ Cufon.registerEngine('canvas', (function() {
|
|||
width += lastWidth = Number(glyph.w || font.w) + letterSpacing;
|
||||
}
|
||||
width = Math.max(maxWidth, width)
|
||||
|
||||
|
||||
if (lastWidth === null) return null; // there's nothing to render
|
||||
|
||||
|
||||
expandRight += (viewBox.width - lastWidth);
|
||||
expandLeft += viewBox.minX;
|
||||
|
||||
|
||||
var wrapper, canvas;
|
||||
|
||||
|
||||
if (redraw) {
|
||||
wrapper = node;
|
||||
canvas = node.firstChild;
|
||||
|
|
@ -704,10 +704,10 @@ Cufon.registerEngine('canvas', (function() {
|
|||
wrapper = document.createElement('span');
|
||||
wrapper.className = 'cufon cufon-canvas';
|
||||
wrapper.alt = text;
|
||||
|
||||
|
||||
canvas = document.createElement('canvas');
|
||||
wrapper.appendChild(canvas);
|
||||
|
||||
|
||||
if (options.printable) {
|
||||
var print = document.createElement('span');
|
||||
print.className = 'cufon-alt';
|
||||
|
|
@ -715,33 +715,32 @@ Cufon.registerEngine('canvas', (function() {
|
|||
wrapper.appendChild(print);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var wStyle = wrapper.style;
|
||||
var cStyle = canvas.style;
|
||||
|
||||
|
||||
var height = size.convert(viewBox.height - expandTop + expandBottom);
|
||||
var roundedHeight = Math.ceil(height);
|
||||
var roundingFactor = roundedHeight / height;
|
||||
|
||||
|
||||
canvas.width = Math.ceil(size.convert(width + expandRight - expandLeft) * roundingFactor);
|
||||
canvas.height = roundedHeight;
|
||||
|
||||
|
||||
// minY has no part in canvas.height
|
||||
|
||||
|
||||
expandTop += viewBox.minY;
|
||||
|
||||
|
||||
cStyle.top = Math.round(size.convert(expandTop - font.ascent)) + 'px';
|
||||
cStyle.left = Math.round(size.convert(expandLeft)) + 'px';
|
||||
|
||||
|
||||
var _width = Math.ceil(size.convert(width * roundingFactor));
|
||||
var wrapperWidth = _width + 'px';
|
||||
|
||||
|
||||
var _height = size.convert(font.height);
|
||||
|
||||
|
||||
Cufon.textOptions.width = _width;
|
||||
Cufon.textOptions.height = _height * lines;
|
||||
Cufon.textOptions.lines = lines;
|
||||
|
||||
|
||||
if (HAS_INLINE_BLOCK) {
|
||||
wStyle.width = wrapperWidth;
|
||||
wStyle.height = _height + 'px';
|
||||
|
|
@ -750,71 +749,93 @@ Cufon.registerEngine('canvas', (function() {
|
|||
wStyle.paddingLeft = wrapperWidth;
|
||||
wStyle.paddingBottom = (_height - 1) + 'px';
|
||||
}
|
||||
|
||||
var g = Cufon.textOptions.context || canvas.getContext('2d'),
|
||||
|
||||
var g = Cufon.textOptions.context || canvas.getContext('2d'),
|
||||
scale = roundedHeight / viewBox.height;
|
||||
|
||||
|
||||
g.save();
|
||||
g.scale(scale, scale);
|
||||
|
||||
|
||||
g.translate(
|
||||
-expandLeft - ((1/scale * canvas.width) / 2) + (Cufon.fonts[font.family].offsetLeft || 0),
|
||||
-expandLeft - ((1/scale * canvas.width) / 2) + (Cufon.fonts[font.family].offsetLeft || 0),
|
||||
-expandTop - (canvas.height / scale * Cufon.textOptions.lines) / 2
|
||||
);
|
||||
|
||||
|
||||
g.lineWidth = font.face['underline-thickness'];
|
||||
|
||||
|
||||
g.save();
|
||||
|
||||
|
||||
function line(y, color) {
|
||||
g.strokeStyle = color;
|
||||
|
||||
|
||||
g.beginPath();
|
||||
|
||||
|
||||
g.moveTo(0, y);
|
||||
g.lineTo(width, y);
|
||||
|
||||
|
||||
g.stroke();
|
||||
}
|
||||
|
||||
|
||||
var textDecoration = options.enableTextDecoration ? Cufon.CSS.textDecoration(el, style) : {},
|
||||
isItalic = options.fontStyle === 'italic';
|
||||
|
||||
|
||||
g.fillStyle = Cufon.textOptions.color || style.get('color');
|
||||
|
||||
if (textDecoration.underline) line(-font.face['underline-position'], g.fillStyle);
|
||||
if (textDecoration.overline) line(font.ascent, g.fillStyle);
|
||||
|
||||
function renderText() {
|
||||
if (isItalic) {
|
||||
g.save();
|
||||
g.transform(1, 0, -0.25, 1, 0, 0);
|
||||
}
|
||||
var left = 0;
|
||||
for (var i = 0, l = chars.length; i < l; ++i) {
|
||||
if (chars[i] === '\n') {
|
||||
// move "caret" to the current left
|
||||
g.translate(-left, -font.ascent - font.ascent / 5 /* space between lines */);
|
||||
left = 0;
|
||||
continue;
|
||||
}
|
||||
var glyph = font.glyphs[chars[i]] || font.missingGlyph;
|
||||
|
||||
if (!glyph) continue;
|
||||
|
||||
var charWidth = Number(glyph.w || font.w) + letterSpacing;
|
||||
|
||||
if (textDecoration) {
|
||||
g.save();
|
||||
g.strokeStyle = g.fillStyle;
|
||||
g.beginPath();
|
||||
if (textDecoration.underline) {
|
||||
g.moveTo(0, -font.face['underline-position']);
|
||||
g.lineTo(charWidth, -font.face['underline-position']);
|
||||
}
|
||||
if (textDecoration.overline) {
|
||||
g.moveTo(0, font.ascent);
|
||||
g.lineTo(charWidth, font.ascent);
|
||||
}
|
||||
if (textDecoration['line-through']) {
|
||||
g.moveTo(0, -font.descent);
|
||||
g.lineTo(charWidth, -font.descent);
|
||||
}
|
||||
g.stroke();
|
||||
g.restore();
|
||||
}
|
||||
|
||||
if (isItalic) {
|
||||
g.save();
|
||||
g.transform(1, 0, -0.25, 1, 0, 0);
|
||||
}
|
||||
|
||||
g.beginPath();
|
||||
if (glyph.d) {
|
||||
if (glyph.code) interpret(glyph.code, g);
|
||||
else glyph.code = generateFromVML('m' + glyph.d, g);
|
||||
}
|
||||
g.fill();
|
||||
var charWidth = Number(glyph.w || font.w) + letterSpacing;
|
||||
|
||||
if (isItalic) {
|
||||
g.restore();
|
||||
}
|
||||
|
||||
g.translate(charWidth, 0);
|
||||
left += charWidth;
|
||||
}
|
||||
if (isItalic) {
|
||||
g.restore();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (shadows) {
|
||||
for (var i = 0, l = shadows.length; i < l; ++i) {
|
||||
var shadow = shadows[i];
|
||||
|
|
@ -825,20 +846,15 @@ Cufon.registerEngine('canvas', (function() {
|
|||
g.restore();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
g.save();
|
||||
renderText();
|
||||
g.restore();
|
||||
|
||||
if (textDecoration['line-through']) {
|
||||
line(-font.descent, g.fillStyle);
|
||||
}
|
||||
|
||||
g.restore();
|
||||
g.restore();
|
||||
|
||||
|
||||
return wrapper;
|
||||
|
||||
|
||||
};
|
||||
|
||||
})());
|
||||
|
|
|
|||
|
|
@ -108,7 +108,8 @@
|
|||
fontFamily: this.fontfamily,
|
||||
enableTextDecoration: true,
|
||||
textDecoration: this.textDecoration,
|
||||
textShadow: this.textShadow
|
||||
textShadow: this.textShadow,
|
||||
fontStyle: this.fontStyle
|
||||
});
|
||||
|
||||
// update width, height
|
||||
|
|
@ -134,7 +135,6 @@
|
|||
// need to specify these manually, since Jaxer doesn't support retrieving computed style
|
||||
el.style.fontSize = '40px';
|
||||
el.style.fontWeight = '400';
|
||||
el.style.fontStyle = 'normal';
|
||||
el.style.letterSpacing = 'normal';
|
||||
el.style.color = '#000000';
|
||||
el.style.fontWeight = '600';
|
||||
|
|
|
|||
|
|
@ -7,10 +7,17 @@ h2 { background: #ffc; margin-top: 0; padding: 5px; color: #333; font-size: 1em;
|
|||
#commands ul { list-style: none; padding-left: 0; }
|
||||
#canvas-console { display: block; font-size: 11px; }
|
||||
#rasterize { margin-top: 10px; color: green; }
|
||||
#complexity { position: absolute; bottom: -20px; left: 0; }
|
||||
#complexity { clear: both; padding-top: 10px; }
|
||||
#controls { margin-bottom: 5px; }
|
||||
#text-controls { clear: both; margin-top: 20px }
|
||||
#text-controls button { vertical-align: top; }
|
||||
#drawing-mode-options { margin-top: 5px; background: #ffc; padding: 5px; border: 1px solid #aaa; }
|
||||
#text { position: relative; top: 30px; width: 250px; height: 100px; }
|
||||
#text { position: relative; width: 250px; height: 100px; }
|
||||
#text-cmd-linethrough { text-decoration: line-through; }
|
||||
#text-cmd-underline { text-decoration: underline; }
|
||||
#text-cmd-overline { text-decoration: overline; }
|
||||
#text-cmd-italic { font-style: italic; }
|
||||
#text-cmd-shadow { text-shadow: rgb(100,100,100) 1px 1px 3px; }
|
||||
|
||||
.canvas-container { margin: 0 auto; float: left; border: 1px solid #aaa; }
|
||||
.clear { color: red; font-weight: bold; margin-top: 1em; }
|
||||
|
|
@ -19,4 +26,6 @@ h2 { background: #ffc; margin-top: 0; padding: 5px; color: #333; font-size: 1em;
|
|||
#drawing-mode.is-drawing { color: red; }
|
||||
|
||||
.svg-shapes { overflow: hidden; width: 320px; }
|
||||
.svg-shapes li { display: inline; }
|
||||
.svg-shapes li { display: inline; }
|
||||
|
||||
button.selected { font-weight: bold; vertical-align: top }
|
||||
|
|
@ -443,4 +443,64 @@
|
|||
};
|
||||
}
|
||||
|
||||
var cmdUnderlineBtn = document.getElementById('text-cmd-underline');
|
||||
if (cmdUnderlineBtn) {
|
||||
cmdUnderlineBtn.onclick = function() {
|
||||
var activeObject = canvas.getActiveObject();
|
||||
if (activeObject && activeObject.type === 'text') {
|
||||
activeObject.textDecoration = (activeObject.textDecoration == 'underline' ? '' : 'underline');
|
||||
this.className = activeObject.textDecoration ? 'selected' : '';
|
||||
canvas.renderAll();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var cmdLinethroughBtn = document.getElementById('text-cmd-linethrough');
|
||||
if (cmdLinethroughBtn) {
|
||||
cmdLinethroughBtn.onclick = function() {
|
||||
var activeObject = canvas.getActiveObject();
|
||||
if (activeObject && activeObject.type === 'text') {
|
||||
activeObject.textDecoration = (activeObject.textDecoration == 'line-through' ? '' : 'line-through');
|
||||
this.className = activeObject.textDecoration ? 'selected' : '';
|
||||
canvas.renderAll();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var cmdOverlineBtn = document.getElementById('text-cmd-overline');
|
||||
if (cmdOverlineBtn) {
|
||||
cmdOverlineBtn.onclick = function() {
|
||||
var activeObject = canvas.getActiveObject();
|
||||
if (activeObject && activeObject.type === 'text') {
|
||||
activeObject.textDecoration = (activeObject.textDecoration == 'overline' ? '' : 'overline');
|
||||
this.className = activeObject.textDecoration ? 'selected' : '';
|
||||
canvas.renderAll();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var cmdItalicBtn = document.getElementById('text-cmd-italic');
|
||||
if (cmdItalicBtn) {
|
||||
cmdItalicBtn.onclick = function() {
|
||||
var activeObject = canvas.getActiveObject();
|
||||
if (activeObject && activeObject.type === 'text') {
|
||||
activeObject.fontStyle = (activeObject.fontStyle == 'italic' ? '' : 'italic');
|
||||
this.className = activeObject.fontStyle ? 'selected' : '';
|
||||
canvas.renderAll();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var cmdShadowBtn = document.getElementById('text-cmd-shadow');
|
||||
if (cmdShadowBtn) {
|
||||
cmdShadowBtn.onclick = function() {
|
||||
var activeObject = canvas.getActiveObject();
|
||||
if (activeObject && activeObject.type === 'text') {
|
||||
activeObject.textShadow = !activeObject.textShadow ? 'rgba(0,0,0,0.2) 2px 2px 10px' : '';
|
||||
this.className = activeObject.fontStyle ? 'selected' : '';
|
||||
canvas.renderAll();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
})(this);
|
||||
|
|
@ -24,10 +24,17 @@
|
|||
|
||||
</div>
|
||||
<div style="position:relative;width:804px;height:704px;float:left;">
|
||||
<canvas id="canvas" width="800" height="700"></canvas>
|
||||
<div id="fps"> </div>
|
||||
<canvas id="canvas" width="800" height="700"></canvas>
|
||||
<div id="complexity">Canvas complexity (number of paths):<strong></strong></div>
|
||||
<textarea id="text"></textarea>
|
||||
<div id="text-controls">
|
||||
<textarea id="text"></textarea>
|
||||
<button type="button" id="text-cmd-italic">Italic</button>
|
||||
<button type="button" id="text-cmd-underline">Underline</button>
|
||||
<button type="button" id="text-cmd-linethrough">Linethrough</button>
|
||||
<button type="button" id="text-cmd-overline">Overline</button>
|
||||
<button type="button" id="text-cmd-shadow">Shadow</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="commands">
|
||||
|
|
|
|||
Loading…
Reference in a new issue