Fix rasterization

This commit is contained in:
Shawn 2018-11-13 16:12:27 -06:00
parent 9b48353355
commit 529eb5b350
6 changed files with 123 additions and 39 deletions

View file

@ -26,7 +26,7 @@ export default class EditableFontface extends Component {
const textRender = fontFace.textRender || DEFAULT_TEXT_RENDER; const textRender = fontFace.textRender || DEFAULT_TEXT_RENDER;
const textStyles = Object.assign({ const textStyles = Object.assign({
minHeight: '30px', minHeight: '20px',
WebkitFontSmoothing: 'antialiased', WebkitFontSmoothing: 'antialiased',
MozOsxFontSmoothing: 'grayscale', MozOsxFontSmoothing: 'grayscale',
}, fontFace.text); }, fontFace.text);
@ -67,5 +67,5 @@ export default class EditableFontface extends Component {
export const PRESETS = { export const PRESETS = {
'Retro Rainbow': require('../FontFaces/RetroRainbow'), 'Retro Rainbow': require('../FontFaces/RetroRainbow'),
'Green Machine': require('../FontFaces/GreenMachine'), 'Green Machine': require('../FontFaces/GreenMachine'),
'Ocean Wave': require('../FontFaces/OceanWave'), 'vapor wave': require('../FontFaces/VaporWave'),
} }

View file

@ -1,21 +0,0 @@
import React from 'react';
module.exports = {
container: {},
editorStyle: {},
text: {},
textRender: (text) => {
// TODO: Inline the path
const id = `curve-${text.replace(/[^A-Za-z0-9]/g, '')}-oceanwave`
return (
<svg viewBox="0 0 425 300" style={{ height: '10em' }}>
<path id={id} fill="transparent" d="M6,150C49.63,93,105.79,36.65,156.2,47.55,207.89,58.74,213,131.91,264,150c40.67,14.43,108.57-6.91,229-145" />
<text x="25">
<textPath href={`#${id}`}>
{text}
</textPath>
</text>
</svg>
);
},
};

View file

@ -3,6 +3,7 @@ module.exports = {
editorStyle: { editorStyle: {
fontFamily: 'Arial, sans-serif', fontFamily: 'Arial, sans-serif',
fontWeight: 'bold', fontWeight: 'bold',
fontSize: '1.2em',
transform: 'scale(1, 1.5)', transform: 'scale(1, 1.5)',
}, },
text: { text: {
@ -10,8 +11,11 @@ module.exports = {
fontWeight: 'bold', fontWeight: 'bold',
backgroundImage: 'linear-gradient(to right, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af)', backgroundImage: 'linear-gradient(to right, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af)',
backgroundClip: 'text', backgroundClip: 'text',
fontSize: '1.2em',
transform: 'scale(1, 1.5)', transform: 'scale(1, 1.5)',
color: 'transparent', color: 'transparent',
paddingBottom: '.25em',
paddingTop: '.1em',
WebkitBackgroundClip: 'text', WebkitBackgroundClip: 'text',
}, },
}; };

View file

@ -0,0 +1,37 @@
import React from 'react';
const charToFullWidth = char => {
const c = char.charCodeAt( 0 )
return c >= 33 && c <= 126
? String.fromCharCode( ( c - 33 ) + 65281 )
: char
}
const stringToFullWidth =
module.exports = {
container: {},
editorStyle: {},
text: {
fontFamily: 'Segoe UI,Helvetica,Arial',
},
textRender: (text) => {
const formattedText = text.toLowerCase().split('').map((char) => {
const c = char.charCodeAt( 0 )
return (c >= 33 && c <= 126) ? String.fromCharCode(c + 65248) : char
}).join('');
// TODO: Inline the path
const id = `curve-${text.replace(/[^A-Za-z0-9]/g, '')}-oceanwave`
return (
<svg viewBox="0 0 500 160" style={{ height: '10em' }}>
<path id={id} fill="transparent" d="M6,150C49.63,93,105.79,36.65,156.2,47.55,207.89,58.74,213,131.91,264,150c40.67,14.43,108.57-6.91,229-145" />
<text x="10">
<textPath href={`#${id}`}>
{formattedText}
</textPath>
</text>
</svg>
);
},
};

View file

@ -49,7 +49,7 @@ export default class RichDraggable extends Component {
return ( return (
<Draggable bounds={bounds} offsetParent={body} cancel=".no-drag"> <Draggable bounds={bounds} offsetParent={body} cancel=".no-drag">
<div ref={me.contents} style={{ padding: '10px', position: 'absolute', border: '4px dashed #ddd', cursor: 'move' }}> <div ref={me.contents} style={{ padding: '10px', position: 'absolute', border: '4px dashed #ddd', cursor: 'move' }} className="creatifyDecor">
<div className="no-drag" style={{ overflow: 'hidden', position: 'relative', cursor: 'auto' }}> <div className="no-drag" style={{ overflow: 'hidden', position: 'relative', cursor: 'auto' }}>
{props.children} {props.children}
</div> </div>

View file

@ -12,20 +12,89 @@ try {
} }
} catch(e) {} } catch(e) {}
const getRasterizedCanvas = (contents, width, height) => {
return new Promise((resolve) => {
// Resolves a bug in Chrome where it renders correctly, but
// replaces the inline styles with an invalid `background-clip`.
if(/Chrome/.test(navigator.userAgent)) {
contents = contents.replace(/background\-clip:(.*)[;$]/g,
(match, group) => (`-webkit-background-clip:${group};${match}`)
);
}
// Attempt to match font kerning with the DOM.
contents = '<style>svg{font-kerning:normal}</style>' + contents;
rasterizeHTML.drawHTML(contents, document.createElement('canvas'), {
width,
height,
}).then((renderResult) => {
const pixelRatio = 2;
// Why do this? Because Firefox doesn't always give us what we expect
// `background-clip: text` is very broken and does not always render in time.
let img = document.createElement('img');
let canvas = document.createElement('canvas');
img.height = canvas.height = height * pixelRatio;
img.width = canvas.width = width * pixelRatio;
canvas.style.height = `${height}px`;
canvas.style.width = `${width}px`;
let shadowNode = document.createElement('div');
Object.assign(shadowNode.style, {
height: 0,
overflow: 'hidden',
width: 0,
});
document.body.appendChild(shadowNode);
shadowNode.appendChild(img);
//document.body.appendChild(canvas);
var svg64 = btoa(unescape(encodeURIComponent(renderResult.svg)));
var b64Start = 'data:image/svg+xml;base64,';
var image64 = b64Start + svg64;
img.addEventListener('load', () => {
window.requestAnimationFrame(() => {
// We still can't trust Firefox's %$%&* engine, add another 5ms timeout
setTimeout(() => {
let context = canvas.getContext('2d', { alpha: false });
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'white';
context.imageSmoothingEnabled = false;
context.scale(pixelRatio, pixelRatio);
context.fillRect(0, 0, canvas.width, canvas.height);
context.drawImage(img, 0, 0);
document.body.removeChild(shadowNode);
resolve(canvas);
}, 10);
});
});
img.src = image64;
});
});
};
export default class Creatify extends Component { export default class Creatify extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
const fontKeys = Object.keys(FontPresets); const fontKeys = Object.keys(FontPresets);
this.canvas = React.createRef();
this.contents = React.createRef(); this.contents = React.createRef();
const fontOptions = fontKeys.map( const fontOptions = fontKeys.map(
(fontName) => ( (fontName) => (
{ {
value: fontName, value: fontName,
label: <EditableFontface key={fontName} fontFace={FontPresets[fontName]} value={fontName} editable="false" />, label: (
<div style={{ maxHeight: '150px', maxWidth: '100%', fontSize: '16px', overflow: 'hidden' }}>
<EditableFontface key={fontName} fontFace={FontPresets[fontName]} value={fontName} editable="false" />
</div>
),
fontName, fontName,
} }
) )
@ -49,19 +118,15 @@ export default class Creatify extends Component {
renderContents() { renderContents() {
const me = this; const me = this;
const canvas = me.canvas.current; let contents = me.contents.current.outerHTML;
let contents = me.contents.current.innerHTML;
// Resolves a bug in Chrome where it renders correctly, but console.log(contents)
// replaces the inline styles with an invalid `background-clip` // Cheap border/handles removal
contents = contents.replace(/background\-clip:(.*)[;$]/g, contents = `<style>.creatifyDecor{border-color:transparent!important;background-color:transparent!important}</style>` + contents;
(match, group) => (`-webkit-background-clip:${group};${match}`)
);
rasterizeHTML.drawHTML(contents, canvas).then((renderResult) => {
}, (error) => {
getRasterizedCanvas(contents, 600, 500).then((element) => {
console.log(element);
document.body.appendChild(element);
}); });
} }
@ -76,10 +141,9 @@ export default class Creatify extends Component {
<div style={{ flex: 1, display: 'flex' }}> <div style={{ flex: 1, display: 'flex' }}>
<div> <div>
<button onClick={() => this.renderContents()}>Rasterize</button> <button onClick={() => this.renderContents()}>Rasterize</button>
<canvas ref={me.canvas} width="200" height="200"></canvas>
<Select isSearchable={false} options={state.fontOptions} onChange={(option) => this.setFont(option.fontName)} /> <Select isSearchable={false} options={state.fontOptions} onChange={(option) => this.setFont(option.fontName)} />
</div> </div>
<div ref={me.contents} style={{ flex: 1 }}> <div ref={me.contents} style={{ height: '600px', width: '500px', fontSize: '22px', overflow: 'hidden', transform: 'translateZ(0)' }}>
<RichDraggable bounds={state.bounds}> <RichDraggable bounds={state.bounds}>
<EditableFontface fontFace={FontPresets[state.fontName]} value="Hello from LBRY" /> <EditableFontface fontFace={FontPresets[state.fontName]} value="Hello from LBRY" />
</RichDraggable> </RichDraggable>