From cf1149b382a1b377b9e6592d216c4a88acdeab90 Mon Sep 17 00:00:00 2001 From: Shawn Date: Sat, 10 Nov 2018 21:57:24 -0600 Subject: [PATCH 01/16] Prototyping --- .../Creatify/EditableFontface/index.js | 68 +++++++++++++++++++ .../components/Creatify/FontFaces/.gitkeep | 0 .../Creatify/FontFaces/GreenMachine.js | 6 ++ .../Creatify/FontFaces/RetroRainbow.js | 18 +++++ .../Creatify/RichDraggable/index.js | 11 +++ client/src/components/Creatify/index.js | 42 ++++++++++++ client/src/pages/HomePage/view.jsx | 3 + 7 files changed, 148 insertions(+) create mode 100644 client/src/components/Creatify/EditableFontface/index.js create mode 100644 client/src/components/Creatify/FontFaces/.gitkeep create mode 100644 client/src/components/Creatify/FontFaces/GreenMachine.js create mode 100644 client/src/components/Creatify/FontFaces/RetroRainbow.js create mode 100644 client/src/components/Creatify/RichDraggable/index.js create mode 100644 client/src/components/Creatify/index.js diff --git a/client/src/components/Creatify/EditableFontface/index.js b/client/src/components/Creatify/EditableFontface/index.js new file mode 100644 index 00000000..4a808d76 --- /dev/null +++ b/client/src/components/Creatify/EditableFontface/index.js @@ -0,0 +1,68 @@ +import React, { Component } from 'react'; + +const DEFAULT_TEXT_RENDER = (text) => text; + +export default class EditableFontface extends Component { + constructor(props) { + super(props); + + this.state = { + value: props.value, + }; + } + + render() { + const me = this; + + const { + value + } = me.state; + + const { + fontFace, + } = me.props; + + const textRender = fontFace.textRender || DEFAULT_TEXT_RENDER; + + const textStyles = { + ...{ + minHeight: '30px', + WebkitFontSmoothing: 'antialiased', + MozOsxFontSmoothing: 'grayscale', + }, + ...(fontFace.text), + }; + + return ( +
+ me.onKeyPress(e)} onChange={(e) => me.onChange(e)} style={{ + ...{ + bottom: 0, + opacity: 0, + padding: 0, + left: 0, + position: 'absolute', + top: 0, + width: '100%', + zIndex: 1, + }, + ...(fontFace.editorStyle || {}), + }} /> +
{textRender(value)}
+
+ ); + } + + onKeyPress(e) { + this.setState({ value: e.target.value }); + } + + onChange(e) { + this.setState({ value: e.target.value }); + } +}; + +export const PRESETS = { + 'Retro Rainbow': require('./FontFaces/RetroRainbow'), + 'Green Machine': require('./FontFaces/GreenMachine'), +} diff --git a/client/src/components/Creatify/FontFaces/.gitkeep b/client/src/components/Creatify/FontFaces/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/client/src/components/Creatify/FontFaces/GreenMachine.js b/client/src/components/Creatify/FontFaces/GreenMachine.js new file mode 100644 index 00000000..24b77af7 --- /dev/null +++ b/client/src/components/Creatify/FontFaces/GreenMachine.js @@ -0,0 +1,6 @@ +export default const GreenMachine = { + container: {}, + text: { + color: 'green', + }, +}; diff --git a/client/src/components/Creatify/FontFaces/RetroRainbow.js b/client/src/components/Creatify/FontFaces/RetroRainbow.js new file mode 100644 index 00000000..dfd15eaf --- /dev/null +++ b/client/src/components/Creatify/FontFaces/RetroRainbow.js @@ -0,0 +1,18 @@ +export default const RetroRainbow = { + container: {}, + editorStyle: { + fontFamily: 'Arial, sans-serif', + fontWeight: 'bold', + transform: 'scale(1, 1.5)', + }, + text: { + fontFamily: 'Arial, sans-serif', + fontWeight: 'bold', + background: 'linear-gradient(to right, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af)', + textFillColor: 'transparent', + backgroundClip: 'text', + transform: 'scale(1, 1.5)', + WebkitTextFillColor: 'transparent', + WebkitBackgroundClip: 'text', + }, +}; diff --git a/client/src/components/Creatify/RichDraggable/index.js b/client/src/components/Creatify/RichDraggable/index.js new file mode 100644 index 00000000..7fa63afb --- /dev/null +++ b/client/src/components/Creatify/RichDraggable/index.js @@ -0,0 +1,11 @@ +import React, { Component } from 'react'; + +export default class RichDraggable extends Component { + render() { + return ( +
+ {this.props.children} +
+ ); + } +}; diff --git a/client/src/components/Creatify/index.js b/client/src/components/Creatify/index.js new file mode 100644 index 00000000..fb45516f --- /dev/null +++ b/client/src/components/Creatify/index.js @@ -0,0 +1,42 @@ +import React, { Component } from 'react'; + +import RichDraggable from './RichDraggable'; +import EditableFontface, { PRESETS as FontPresets } from './EditableFontface'; + +export default class Creatify extends Component { + constructor(props) { + super(props); + + const fontKeys = Object.keys(FontPresets); + + this.state = { + fontName: fontKeys[0], + fontOptions: fontKeys.map((fontName) => ( + + )), + }; + } + + render() { + const { + state, + } = this; + + return ( +
+ + + + +
+ ); + } + + onChangeFont(event) { + this.setState({ + fontName: event.target.value, + }); + } +}; diff --git a/client/src/pages/HomePage/view.jsx b/client/src/pages/HomePage/view.jsx index 36b8a78e..808cc826 100644 --- a/client/src/pages/HomePage/view.jsx +++ b/client/src/pages/HomePage/view.jsx @@ -3,6 +3,8 @@ import PageLayout from '@components/PageLayout'; import PublishTool from '@containers/PublishTool'; import ContentPageWrapper from '@pages/ContentPageWrapper'; +import Creatify from '@components/Creatify'; + class HomePage extends React.Component { componentWillUnmount () { this.props.clearFile(); @@ -16,6 +18,7 @@ class HomePage extends React.Component { pageTitle={'Speech'} pageUri={''} > + ); From ba19d4311dbf125e9633c876dfec3f93bb381b42 Mon Sep 17 00:00:00 2001 From: Shawn Date: Mon, 12 Nov 2018 17:00:35 -0600 Subject: [PATCH 02/16] Update branch --- .../Creatify/EditableFontface/index.js | 21 ++++--- .../Creatify/FontFaces/GreenMachine.js | 2 +- .../Creatify/FontFaces/RetroRainbow.js | 7 +-- .../Creatify/RichDraggable/index.js | 57 ++++++++++++++++++- client/src/components/Creatify/index.js | 51 +++++++++++++++-- 5 files changed, 114 insertions(+), 24 deletions(-) diff --git a/client/src/components/Creatify/EditableFontface/index.js b/client/src/components/Creatify/EditableFontface/index.js index 4a808d76..4050413c 100644 --- a/client/src/components/Creatify/EditableFontface/index.js +++ b/client/src/components/Creatify/EditableFontface/index.js @@ -24,14 +24,13 @@ export default class EditableFontface extends Component { const textRender = fontFace.textRender || DEFAULT_TEXT_RENDER; - const textStyles = { - ...{ - minHeight: '30px', - WebkitFontSmoothing: 'antialiased', - MozOsxFontSmoothing: 'grayscale', - }, - ...(fontFace.text), - }; + const textStyles = Object.assign({ + minHeight: '30px', + WebkitFontSmoothing: 'antialiased', + MozOsxFontSmoothing: 'grayscale', + }, fontFace.text); + + console.log(textStyles); return (
@@ -48,7 +47,7 @@ export default class EditableFontface extends Component { }, ...(fontFace.editorStyle || {}), }} /> -
{textRender(value)}
+
{textRender(value)}
); } @@ -63,6 +62,6 @@ export default class EditableFontface extends Component { }; export const PRESETS = { - 'Retro Rainbow': require('./FontFaces/RetroRainbow'), - 'Green Machine': require('./FontFaces/GreenMachine'), + 'Retro Rainbow': require('../FontFaces/RetroRainbow'), + 'Green Machine': require('../FontFaces/GreenMachine'), } diff --git a/client/src/components/Creatify/FontFaces/GreenMachine.js b/client/src/components/Creatify/FontFaces/GreenMachine.js index 24b77af7..b3163983 100644 --- a/client/src/components/Creatify/FontFaces/GreenMachine.js +++ b/client/src/components/Creatify/FontFaces/GreenMachine.js @@ -1,4 +1,4 @@ -export default const GreenMachine = { +module.exports = { container: {}, text: { color: 'green', diff --git a/client/src/components/Creatify/FontFaces/RetroRainbow.js b/client/src/components/Creatify/FontFaces/RetroRainbow.js index dfd15eaf..c1dd4d8d 100644 --- a/client/src/components/Creatify/FontFaces/RetroRainbow.js +++ b/client/src/components/Creatify/FontFaces/RetroRainbow.js @@ -1,4 +1,4 @@ -export default const RetroRainbow = { +module.exports = { container: {}, editorStyle: { fontFamily: 'Arial, sans-serif', @@ -8,11 +8,10 @@ export default const RetroRainbow = { text: { fontFamily: 'Arial, sans-serif', fontWeight: 'bold', - background: 'linear-gradient(to right, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af)', - textFillColor: 'transparent', + backgroundImage: 'linear-gradient(to right, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af)', backgroundClip: 'text', transform: 'scale(1, 1.5)', - WebkitTextFillColor: 'transparent', + color: 'transparent', WebkitBackgroundClip: 'text', }, }; diff --git a/client/src/components/Creatify/RichDraggable/index.js b/client/src/components/Creatify/RichDraggable/index.js index 7fa63afb..b4de955b 100644 --- a/client/src/components/Creatify/RichDraggable/index.js +++ b/client/src/components/Creatify/RichDraggable/index.js @@ -1,11 +1,62 @@ import React, { Component } from 'react'; +import Draggable from 'react-draggable'; + +let body; +try { + body = document.body; +} catch(e) {} export default class RichDraggable extends Component { + constructor(props) { + super(props); + + this.contents = React.createRef(); + this.state = { + height: 0, + width: 0, + }; + } + + componentDidMount() { + const height = this.contents.current.offsetHeight; + const width = this.contents.current.offsetWidth; + + this.setState({ + height, + width, + }); + } + render() { + const me = this; + + const { + props, + state, + } = me; + + const { + height: bottom, + width: right, + } = props.bounds; + + const bounds = { + top: 0, + left: 0, + right: right - state.width, + bottom: bottom - state.height, + }; + + console.log(bounds); + return ( -
- {this.props.children} -
+ +
+
+ {props.children} +
+
+
); } }; diff --git a/client/src/components/Creatify/index.js b/client/src/components/Creatify/index.js index fb45516f..01217a56 100644 --- a/client/src/components/Creatify/index.js +++ b/client/src/components/Creatify/index.js @@ -3,33 +3,74 @@ import React, { Component } from 'react'; import RichDraggable from './RichDraggable'; import EditableFontface, { PRESETS as FontPresets } from './EditableFontface'; +// TODO: Remove `rasterizehtml` from SSR +let rasterizeHTML = () => {}; +try { + if(window) { + rasterizeHTML = require('rasterizehtml') + } +} catch(e) {} + export default class Creatify extends Component { constructor(props) { super(props); const fontKeys = Object.keys(FontPresets); + this.canvas = React.createRef(); + this.contents = React.createRef(); + this.state = { + bounds: {}, fontName: fontKeys[0], fontOptions: fontKeys.map((fontName) => ( - + )), }; } + componentDidMount() { + const bounds = this.contents.current.getBoundingClientRect(); + + this.setState({ + bounds, + }); + } + + renderContents() { + const me = this; + + const canvas = me.canvas.current; + let contents = me.contents.current.innerHTML; + + // Resolves a bug in Chrome where it renders correctly, but + // replaces the inline styles with an invalid `background-clip` + contents = contents.replace(/background\-clip:(.*)[;$]/g, + (match, group) => (`-webkit-background-clip:${group};${match}`) + ); + + rasterizeHTML.drawHTML(contents, canvas); + } + render() { + const me = this; + const { state, } = this; return ( -
+
+ + - - - +
+ + + +
); } From 1c44a69912d8a2f27a4b36cc47d240d1de673f4b Mon Sep 17 00:00:00 2001 From: Shawn Date: Mon, 12 Nov 2018 18:53:00 -0600 Subject: [PATCH 03/16] Update branch --- .../Creatify/EditableFontface/index.js | 31 ++++++++-------- client/src/components/Creatify/index.js | 35 +++++++++++++------ 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/client/src/components/Creatify/EditableFontface/index.js b/client/src/components/Creatify/EditableFontface/index.js index 4050413c..7f7664d7 100644 --- a/client/src/components/Creatify/EditableFontface/index.js +++ b/client/src/components/Creatify/EditableFontface/index.js @@ -19,6 +19,7 @@ export default class EditableFontface extends Component { } = me.state; const { + editable = true, fontFace, } = me.props; @@ -30,23 +31,25 @@ export default class EditableFontface extends Component { MozOsxFontSmoothing: 'grayscale', }, fontFace.text); - console.log(textStyles); + const fontInput = (editable === true) ? ( + me.onKeyPress(e)} onChange={(e) => me.onChange(e)} style={{ + ...{ + bottom: 0, + opacity: 0, + padding: 0, + left: 0, + position: 'absolute', + top: 0, + width: '100%', + zIndex: 1, + }, + ...(fontFace.editorStyle || {}), + }} /> + ) : null; return (
- me.onKeyPress(e)} onChange={(e) => me.onChange(e)} style={{ - ...{ - bottom: 0, - opacity: 0, - padding: 0, - left: 0, - position: 'absolute', - top: 0, - width: '100%', - zIndex: 1, - }, - ...(fontFace.editorStyle || {}), - }} /> + {fontInput}
{textRender(value)}
); diff --git a/client/src/components/Creatify/index.js b/client/src/components/Creatify/index.js index 01217a56..013d1ff7 100644 --- a/client/src/components/Creatify/index.js +++ b/client/src/components/Creatify/index.js @@ -1,4 +1,5 @@ import React, { Component } from 'react'; +import Select from 'react-select' import RichDraggable from './RichDraggable'; import EditableFontface, { PRESETS as FontPresets } from './EditableFontface'; @@ -20,12 +21,20 @@ export default class Creatify extends Component { this.canvas = React.createRef(); this.contents = React.createRef(); + const fontOptions = fontKeys.map( + (fontName) => ( + { + value: fontName, + label: , + fontName, + } + ) + ); + this.state = { bounds: {}, fontName: fontKeys[0], - fontOptions: fontKeys.map((fontName) => ( - - )), + fontOptions, }; } @@ -59,13 +68,19 @@ export default class Creatify extends Component { state, } = this; + const options = [ + { value: 'chocolate', label:
Chocolate
}, + { value: 'strawberry', label: 'Strawberry' }, + { value: 'vanilla', label: 'Vanilla' }, + ]; + return (
- - - +
+ + + this.setFont(option.fontName)} /> + this.setFont(option.fontName)} />
-
+
From 63d1de3147397d30ca577739e61d665b465b96d2 Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 14 Nov 2018 02:43:47 -0600 Subject: [PATCH 06/16] Refactor, fix rendering edge cases --- .../Creatify/EditableFontface/index.js | 1 + .../Creatify/FontFaces/TheSpecial.js | 41 +++++++ client/src/components/Creatify/index.js | 108 ++++++++---------- 3 files changed, 92 insertions(+), 58 deletions(-) create mode 100644 client/src/components/Creatify/FontFaces/TheSpecial.js diff --git a/client/src/components/Creatify/EditableFontface/index.js b/client/src/components/Creatify/EditableFontface/index.js index 603cb8dd..29866c65 100644 --- a/client/src/components/Creatify/EditableFontface/index.js +++ b/client/src/components/Creatify/EditableFontface/index.js @@ -68,4 +68,5 @@ export const PRESETS = { 'Retro Rainbow': require('../FontFaces/RetroRainbow'), 'Green Machine': require('../FontFaces/GreenMachine'), 'vapor wave': require('../FontFaces/VaporWave'), + 'The Special': require('../FontFaces/TheSpecial'), } diff --git a/client/src/components/Creatify/FontFaces/TheSpecial.js b/client/src/components/Creatify/FontFaces/TheSpecial.js new file mode 100644 index 00000000..d7e5087a --- /dev/null +++ b/client/src/components/Creatify/FontFaces/TheSpecial.js @@ -0,0 +1,41 @@ +import React from 'react'; + +module.exports = { + container: {}, + editorStyle: { + fontFamily: 'Arial, sans-serif', + fontWeight: 'bold', + fontSize: '1.4em', + }, + text: { + fontFamily: 'Arial, sans-serif', + fontWeight: 'bold', + backgroundImage: 'linear-gradient(to right, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af)', + backgroundClip: 'text', + fontSize: '1.4em', + color: 'transparent', + paddingBottom: '.25em', + paddingTop: '.1em', + WebkitBackgroundClip: 'text', + }, + textRender: (text) => { + text = text + .replace(/love [^\s.!$]+/g, 'love LBRY') + .replace(/LBRY/g, 'amazing LBRY') + .replace(/julie/gi, 'super Julie') + .replace(/tom/gi, 'amazing Tom') + .replace(/(btc|bch)/gi, 'LBC') + .replace(/\w+ is \w+/gi, 'LBRY is amazing'); + + return text.split(/chris[\d\w]*/gi).reduce((result, value, index) => { + if(index !== 0) { + result.push(); + } + result.push({value}) + + return result; + }, []) + }, +}; + +const THE_FACE = 'data:image/png;base64,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'; diff --git a/client/src/components/Creatify/index.js b/client/src/components/Creatify/index.js index 9d35e5e6..cc0a54a7 100644 --- a/client/src/components/Creatify/index.js +++ b/client/src/components/Creatify/index.js @@ -4,77 +4,71 @@ import Select from 'react-select' import RichDraggable from './RichDraggable'; import EditableFontface, { PRESETS as FontPresets } from './EditableFontface'; -// TODO: Remove `rasterizehtml` from SSR -let rasterizeHTML = () => {}; -try { - if(window) { - rasterizeHTML = require('rasterizehtml') - } -} catch(e) {} - const getRasterizedCanvas = (contents, width, height) => { return new Promise((resolve) => { + // Parse to xHTML for SVG/foreignObject rendering + contents = new XMLSerializer().serializeToString( + new DOMParser().parseFromString(contents, 'text/html') + ); + // 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}`) + contents = contents.replace(/background\-clip:(\s*text\s*)[;$]/g, + (match, group) => (`-webkit-background-clip:text;${match}`) ); } // Attempt to match font kerning with the DOM. contents = '' + contents; + const svgContents = ` + +${contents} +`; - rasterizeHTML.drawHTML(contents, document.createElement('canvas'), { - width, - height, - }).then((renderResult) => { - const pixelRatio = 2; + 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'); + 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`; + 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; + let shadowNode = document.createElement('div'); + Object.assign(shadowNode.style, { + height: 0, + overflow: 'hidden', + width: 0, }); + document.body.appendChild(shadowNode); + + shadowNode.appendChild(img); + + var svg64 = btoa(unescape(encodeURIComponent(svgContents))); + 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 + // `background-clip: text` is very broken and does not always render in time. + 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; }); }; @@ -120,12 +114,10 @@ export default class Creatify extends Component { let contents = me.contents.current.outerHTML; - console.log(contents) // Cheap border/handles removal contents = `` + contents; getRasterizedCanvas(contents, 600, 500).then((element) => { - console.log(element); document.body.appendChild(element); }); } From 8ee57c25e4cb8dc9a27d23c08ccf4a0432ad2789 Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 14 Nov 2018 16:06:52 -0600 Subject: [PATCH 07/16] Code cleanup, implement some editing --- client/src/components/Creatify/index.js | 3 +- client/src/containers/Dropzone/view.jsx | 119 ++++++++++++++++++------ client/src/pages/HomePage/view.jsx | 3 - 3 files changed, 90 insertions(+), 35 deletions(-) diff --git a/client/src/components/Creatify/index.js b/client/src/components/Creatify/index.js index cc0a54a7..867183f0 100644 --- a/client/src/components/Creatify/index.js +++ b/client/src/components/Creatify/index.js @@ -124,8 +124,8 @@ export default class Creatify extends Component { render() { const me = this; - const { + props, state, } = this; @@ -139,6 +139,7 @@ export default class Creatify extends Component { + {props.children}
); diff --git a/client/src/containers/Dropzone/view.jsx b/client/src/containers/Dropzone/view.jsx index b524b014..3d9f2743 100644 --- a/client/src/containers/Dropzone/view.jsx +++ b/client/src/containers/Dropzone/view.jsx @@ -1,5 +1,6 @@ import React from 'react'; import { validateFile } from '../../utils/file'; +import Creatify from '@components/Creatify'; import DropzonePreviewImage from '@components/DropzonePreviewImage'; import DropzoneDropItDisplay from '@components/DropzoneDropItDisplay'; import DropzoneInstructionsDisplay from '@components/DropzoneInstructionsDisplay'; @@ -7,11 +8,21 @@ import DropzoneInstructionsDisplay from '@components/DropzoneInstructionsDisplay class Dropzone extends React.Component { constructor (props) { super(props); + this.state = { - dragOver : false, - mouseOver : false, - dimPreview: false, + dragOver : false, + mouseOver : false, + dimPreview : false, + filePreview: null, }; + + if(props.file) { + // No side effects allowed with `getDerivedStateFromProps`, so + // we must use `componentDidUpdate` and `constructor` routines. + // Note: `FileReader` has an `onloadend` side-effect + this.updateFilePreview(); + } + this.handleDrop = this.handleDrop.bind(this); this.handleDragOver = this.handleDragOver.bind(this); this.handleDragEnd = this.handleDragEnd.bind(this); @@ -23,6 +34,25 @@ class Dropzone extends React.Component { this.handleFileInput = this.handleFileInput.bind(this); this.chooseFile = this.chooseFile.bind(this); } + + componentDidUpdate(prevProps) { + if(prevProps.file !== this.props.file) { + this.updateFilePreview(); + } + } + + updateFilePreview() { + if (this.props.file) { + const fileReader = new FileReader(); + fileReader.readAsDataURL(this.props.file); + fileReader.onloadend = () => { + this.setState({ + filePreview: fileReader.result + }); + }; + } + } + handleDrop (event) { event.preventDefault(); this.setState({dragOver: false}); @@ -35,9 +65,11 @@ class Dropzone extends React.Component { } } } + handleDragOver (event) { event.preventDefault(); } + handleDragEnd (event) { var dt = event.dataTransfer; if (dt.items) { @@ -48,27 +80,34 @@ class Dropzone extends React.Component { event.dataTransfer.clearData(); } } + handleDragEnter () { this.setState({dragOver: true, dimPreview: true}); } + handleDragLeave () { this.setState({dragOver: false, dimPreview: false}); } + handleMouseEnter () { this.setState({mouseOver: true, dimPreview: true}); } + handleMouseLeave () { this.setState({mouseOver: false, dimPreview: false}); } + handleClick (event) { event.preventDefault(); document.getElementById('file_input').click(); } + handleFileInput (event) { event.preventDefault(); const fileList = event.target.files; this.chooseFile(fileList[0]); } + chooseFile (file) { if (file) { try { @@ -80,9 +119,49 @@ class Dropzone extends React.Component { this.props.selectFile(file); } } + render () { - const { dragOver, mouseOver, dimPreview } = this.state; + const { dragOver, mouseOver, dimPreview, filePreview } = this.state; const { file, thumbnail, fileError, isUpdate, sourceUrl, fileExt } = this.props; + + const hasContent = !!(file || isUpdate); + + const dropZoneHooks = file ? {} : { + onDrop: this.handleDrop, + onDragOver: this.handleDragOver, + onDragEnd: this.handleDragEnd, + onDragEnter: this.handleDragEnter, + onDragLeave: this.handleDragLeave, + onMouseEnter: this.handleMouseEnter, + onMouseLeave: this.handleMouseLeave, + onClick: this.handleClick, + }; + + const dropZonePreviewProps = file ? { + dimPreview, + file, + thumbnail, + } : { + dimPreview: true, + isUpdate: true, + sourceUrl, + }; + + let memeify = file && filePreview ? ( + + + + ) : null; + + console.log({ + file, + thumbnail, + sourceUrl, + filePreview, + }); + + const dropZoneClassName = 'dropzone' + (dragOver ? ' dropzone--drag-over' : ''); + return ( {isUpdate && fileExt === 'mp4' ? ( @@ -100,31 +179,10 @@ class Dropzone extends React.Component { encType='multipart/form-data' /> -
- {file || isUpdate ? ( +
+ {hasContent ? (
- {file ? ( - - ) : ( - - )} +
{ dragOver ? : null } { mouseOver ? ( @@ -133,13 +191,12 @@ class Dropzone extends React.Component { message={fileExt === 'mp4' ? 'Drag & drop new thumbnail' : null} /> ) : null } + {memeify}
) : ( dragOver ? : ( - + ) )}
diff --git a/client/src/pages/HomePage/view.jsx b/client/src/pages/HomePage/view.jsx index 808cc826..36b8a78e 100644 --- a/client/src/pages/HomePage/view.jsx +++ b/client/src/pages/HomePage/view.jsx @@ -3,8 +3,6 @@ import PageLayout from '@components/PageLayout'; import PublishTool from '@containers/PublishTool'; import ContentPageWrapper from '@pages/ContentPageWrapper'; -import Creatify from '@components/Creatify'; - class HomePage extends React.Component { componentWillUnmount () { this.props.clearFile(); @@ -18,7 +16,6 @@ class HomePage extends React.Component { pageTitle={'Speech'} pageUri={''} > - ); From c5257f90e1b555b458a98d9f518d4cd7f8c95a8c Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 14 Nov 2018 18:54:30 -0600 Subject: [PATCH 08/16] Fix flex/dimensions, begin adding toolbar --- client/src/components/Creatify/index.js | 32 +++++++++++++++++++++---- client/src/containers/Dropzone/view.jsx | 13 ++++------ package.json | 3 +++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/client/src/components/Creatify/index.js b/client/src/components/Creatify/index.js index 867183f0..c1ea9e4b 100644 --- a/client/src/components/Creatify/index.js +++ b/client/src/components/Creatify/index.js @@ -1,9 +1,17 @@ +import { library } from '@fortawesome/fontawesome-svg-core' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' + import React, { Component } from 'react'; import Select from 'react-select' import RichDraggable from './RichDraggable'; import EditableFontface, { PRESETS as FontPresets } from './EditableFontface'; +import { + faFont +} from '@fortawesome/free-solid-svg-icons' +library.add(faFont); + const getRasterizedCanvas = (contents, width, height) => { return new Promise((resolve) => { // Parse to xHTML for SVG/foreignObject rendering @@ -102,22 +110,36 @@ export default class Creatify extends Component { } componentDidMount() { + // TODO: Fix bounds + /* const bounds = this.contents.current.getBoundingClientRect(); this.setState({ bounds, }); + + console.log({ + bounds + }) + */ } renderContents() { const me = this; - let contents = me.contents.current.outerHTML; + const contentsElement = me.contents.current; + let contents = contentsElement.outerHTML; // Cheap border/handles removal contents = `` + contents; - getRasterizedCanvas(contents, 600, 500).then((element) => { + const contentsWidth = contentsElement.offsetWidth; + const contentsHeight = contentsElement.offsetHeight; + + // Fix the dimensions, fixes when flex is used. + contents = `
${contents}
`; + + getRasterizedCanvas(contents, contentsWidth, contentsHeight).then((element) => { document.body.appendChild(element); }); } @@ -130,12 +152,12 @@ export default class Creatify extends Component { } = this; return ( -
-
+
+
this.setFont(option.fontName)} /> + */}
diff --git a/client/src/containers/Dropzone/view.jsx b/client/src/containers/Dropzone/view.jsx index 161e8736..4814e4e7 100644 --- a/client/src/containers/Dropzone/view.jsx +++ b/client/src/containers/Dropzone/view.jsx @@ -1,10 +1,15 @@ import React from 'react'; + import { validateFile } from '../../utils/file'; import Creatify from '@components/Creatify'; import DropzonePreviewImage from '@components/DropzonePreviewImage'; import DropzoneDropItDisplay from '@components/DropzoneDropItDisplay'; import DropzoneInstructionsDisplay from '@components/DropzoneInstructionsDisplay'; +import { library } from '@fortawesome/fontawesome-svg-core' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faEdit } from '@fortawesome/free-solid-svg-icons'; + class Dropzone extends React.Component { constructor (props) { super(props); @@ -14,6 +19,7 @@ class Dropzone extends React.Component { mouseOver : false, dimPreview : false, filePreview: null, + memeify : false, }; if(props.file) { @@ -121,7 +127,7 @@ class Dropzone extends React.Component { } render () { - const { dragOver, mouseOver, dimPreview, filePreview } = this.state; + const { dragOver, mouseOver, dimPreview, filePreview, memeify } = this.state; const { file, thumbnail, fileError, isUpdate, sourceUrl, fileExt } = this.props; const hasContent = !!(file || isUpdate); @@ -147,7 +153,7 @@ class Dropzone extends React.Component { sourceUrl, }; - let memeify = file && filePreview ? ( + const memeifyContent = memeify && file && filePreview ? (
@@ -186,7 +192,12 @@ class Dropzone extends React.Component { message={fileExt === 'mp4' ? 'Drag & drop new thumbnail' : null} /> ) : null } - {memeify} + { !memeify && ( +
this.setState({ memeify: !memeify })}> + Memeify +
+ )} + {memeifyContent}
) : ( diff --git a/package.json b/package.json index 6cf18c8b..8f066fae 100644 --- a/package.json +++ b/package.json @@ -70,12 +70,14 @@ "passport-local": "^1.0.0", "prop-types": "^15.6.2", "react": "^16.4.2", - "react-feather": "^1.1.4", "react-dom": "^16.6.1", + "react-draggable": "^3.0.5", + "react-feather": "^1.1.4", "react-ga": "^2.5.3", "react-helmet": "^5.2.0", "react-redux": "^5.1.1", "react-router-dom": "^4.3.1", + "react-select": "^2.1.1", "redux": "^4.0.1", "redux-saga": "^0.16.2", "sequelize": "^4.41.1", @@ -97,9 +99,9 @@ "@babel/preset-react": "^7.0.0", "@babel/preset-stage-2": "^7.0.0", "@babel/register": "^7.0.0", + "babel-eslint": "9.0.0-beta.3", "babel-loader": "^7.1.2", "babel-plugin-module-resolver": "^3.1.1", - "babel-eslint": "9.0.0-beta.3", "builder": "^4.0.0", "chai": "^4.2.0", "chai-http": "^4.2.0", From b879baa3c863f58a7d74d03be5daa46699d71b30 Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 14 Nov 2018 21:24:36 -0600 Subject: [PATCH 10/16] Add toolbar base UI --- client/scss/_dropzone.scss | 20 +++++++++++++++--- client/src/components/Creatify/index.js | 28 ++++++++++++++++--------- client/src/containers/Dropzone/view.jsx | 16 +++++++------- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/client/scss/_dropzone.scss b/client/scss/_dropzone.scss index b48bf165..3c5feb35 100644 --- a/client/scss/_dropzone.scss +++ b/client/scss/_dropzone.scss @@ -4,6 +4,7 @@ // be a flex container for children display: flex; flex-direction: column; + position: relative; } .dropzone { @@ -12,6 +13,7 @@ flex: 1 0 auto; // be a flex container for children display: flex; + padding: 1em; -webkit-flex-direction: column; flex-direction: column; justify-content: center; @@ -52,13 +54,17 @@ .dropzone-preview-image { display: block; - padding: 1em; - width: calc(100% - 2em); + width: 100%; +} + +.dropzone-preview-memeify { + margin-top: 3em; } .dropzone-memeify-button { - background: #333; + background: $primary-color; color: #fff; + cursor: pointer; font-size: .8em; padding: 3px 6px; position: absolute; @@ -67,6 +73,14 @@ z-index: 3; } +.dropzone-memeify-toolbar { + /* TODO: Cleanup `!important` */ + background: $primary-color !important; + left: -1em !important; + right: -1em !important; + top: -4em !important; +} + .dropzone-instructions-display__chooser-label { text-decoration: underline; } diff --git a/client/src/components/Creatify/index.js b/client/src/components/Creatify/index.js index c7460038..35248408 100644 --- a/client/src/components/Creatify/index.js +++ b/client/src/components/Creatify/index.js @@ -8,11 +8,10 @@ import RichDraggable from './RichDraggable'; import EditableFontface, { PRESETS as FontPresets } from './EditableFontface'; import { - faEdit, faFont, + faMinusCircle, + faPlusCircle, } from '@fortawesome/free-solid-svg-icons'; -library.add(faEdit); -library.add(faFont); const getRasterizedCanvas = (contents, width, height) => { return new Promise((resolve) => { @@ -30,10 +29,10 @@ const getRasterizedCanvas = (contents, width, height) => { } // Attempt to match font kerning with the DOM. - contents = '' + contents; + const kerningAndPadding = ''; const svgContents = ` -${contents} +${kerningAndPadding}${contents} `; const pixelRatio = 2; @@ -153,13 +152,22 @@ export default class Creatify extends Component { state, } = this; + // TODO: Abstract into separate package & use CSS Modules. + const spacerCss = { width: '.3em' }; return (
-
- {/* - - this.setFont(option.fontName)} /> +
+
+
this.renderContents()} style={{ alignItems: 'center', alignSelf: 'stretch', color: '#fff', border: '1px solid #fff', display: 'flex', padding: '.4em' }}> + Finish +
diff --git a/client/src/containers/Dropzone/view.jsx b/client/src/containers/Dropzone/view.jsx index 4814e4e7..4352a74b 100644 --- a/client/src/containers/Dropzone/view.jsx +++ b/client/src/containers/Dropzone/view.jsx @@ -154,7 +154,7 @@ class Dropzone extends React.Component { }; const memeifyContent = memeify && file && filePreview ? ( - +
@@ -168,7 +168,12 @@ class Dropzone extends React.Component { {isUpdate && fileExt === 'mp4' ? (

Video updates are currently disabled. This feature will be available soon. You can edit metadata.

) : ( -
+
+ { hasContent && !memeify && ( +
this.setState({ memeify: !memeify })}> + Memeify +
+ )}
{hasContent ? ( -
+
{ dragOver ? : null } @@ -192,11 +197,6 @@ class Dropzone extends React.Component { message={fileExt === 'mp4' ? 'Drag & drop new thumbnail' : null} /> ) : null } - { !memeify && ( -
this.setState({ memeify: !memeify })}> - Memeify -
- )} {memeifyContent}
From 73008347ca364649aa9c6191c38b996c6dd1691f Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 14 Nov 2018 22:17:18 -0600 Subject: [PATCH 11/16] Add dynamic elements --- .../Creatify/RichDraggable/index.js | 2 +- client/src/components/Creatify/index.js | 71 ++++++++++++++++--- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/client/src/components/Creatify/RichDraggable/index.js b/client/src/components/Creatify/RichDraggable/index.js index a4097085..8ec02ede 100644 --- a/client/src/components/Creatify/RichDraggable/index.js +++ b/client/src/components/Creatify/RichDraggable/index.js @@ -48,7 +48,7 @@ export default class RichDraggable extends Component { }; return ( - +
{props.children} diff --git a/client/src/components/Creatify/index.js b/client/src/components/Creatify/index.js index 35248408..5715ce23 100644 --- a/client/src/components/Creatify/index.js +++ b/client/src/components/Creatify/index.js @@ -104,8 +104,10 @@ export default class Creatify extends Component { ); this.state = { + activeElement: false, bounds: {}, fontName: fontKeys[0], + elements: [], fontOptions, }; } @@ -125,7 +127,58 @@ export default class Creatify extends Component { */ } - renderContents() { + setActiveElement(activeElement) { + this.setState({ activeElement }); + } + + addElement() { + const { + state + } = this; + + const newElementKey = `element-${state.elements.length}-${Date.now()}`; + + const newElement = ( + this.setActiveElement(newElement)}> + + + ); + + this.setState({ + elements: [...state.elements, newElement], + activeElement: newElement, + }); + } + + removeElement() { + const { + state + } = this; + + const activeElementIndex = state.elements.indexOf(state.activeElement); + + if(state.elements.length === 0 || activeElementIndex === -1) { + return; + } + + const elements = [...state.elements]; + elements.splice(activeElementIndex, 1) + + this.setState({ + activeElement: false, + elements, + }); + } + + async onSave() { + const renderedCanvas = await this.renderContentsToCanvas(); + + if(this.props.onSave) { + this.props.onSave(renderedCanvas); + } + } + + async renderContentsToCanvas() { const me = this; const contentsElement = me.contents.current; @@ -140,9 +193,7 @@ export default class Creatify extends Component { // Fix the dimensions, fixes when flex is used. contents = `
${contents}
`; - getRasterizedCanvas(contents, contentsWidth, contentsHeight).then((element) => { - document.body.appendChild(element); - }); + return await getRasterizedCanvas(contents, contentsWidth, contentsHeight); } render() { @@ -157,22 +208,20 @@ export default class Creatify extends Component { return (
- + this.addElement()} />
- + this.removeElement()} />
this.setFont(option.fontName)} />
-
this.onSave()} style={{ alignItems: 'center', alignSelf: 'stretch', color: '#fff', border: '1px solid #fff', display: 'flex', padding: '0 0.6em' }}> +
this.onSave()} style={{ alignItems: 'center', alignSelf: 'stretch', border: '1px solid #fff', borderRadius: '4px', color: '#fff', display: 'flex', padding: '0 0.6em' }}> Save
diff --git a/client/src/containers/Dropzone/view.jsx b/client/src/containers/Dropzone/view.jsx index 4352a74b..db9bdc66 100644 --- a/client/src/containers/Dropzone/view.jsx +++ b/client/src/containers/Dropzone/view.jsx @@ -126,6 +126,23 @@ class Dropzone extends React.Component { } } + selectFileFromCanvas (canvas) { + const destinationFormat = 'image/jpeg'; + + canvas.toBlob((blob) => { + const file = new File([blob], 'memeify.jpg', { + type: destinationFormat, + }); + + this.props.selectFile(file); + + // TODO: Add ability to reset. + this.setState({ + memeify: false, + }); + }, destinationFormat, 0.95); + } + render () { const { dragOver, mouseOver, dimPreview, filePreview, memeify } = this.state; const { file, thumbnail, fileError, isUpdate, sourceUrl, fileExt } = this.props; @@ -154,7 +171,7 @@ class Dropzone extends React.Component { }; const memeifyContent = memeify && file && filePreview ? ( - + this.selectFileFromCanvas(canvas)}>
From 010734629ec9a123b4a76c37044ea76fe6771dcf Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 15 Nov 2018 02:20:07 -0600 Subject: [PATCH 13/16] Add OldBlue font --- .../Creatify/EditableFontface/index.js | 1 + .../components/Creatify/FontFaces/OldBlue.js | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 client/src/components/Creatify/FontFaces/OldBlue.js diff --git a/client/src/components/Creatify/EditableFontface/index.js b/client/src/components/Creatify/EditableFontface/index.js index 29866c65..d2f0e18d 100644 --- a/client/src/components/Creatify/EditableFontface/index.js +++ b/client/src/components/Creatify/EditableFontface/index.js @@ -69,4 +69,5 @@ export const PRESETS = { 'Green Machine': require('../FontFaces/GreenMachine'), 'vapor wave': require('../FontFaces/VaporWave'), 'The Special': require('../FontFaces/TheSpecial'), + 'Old Blue': require('../FontFaces/OldBlue'), } diff --git a/client/src/components/Creatify/FontFaces/OldBlue.js b/client/src/components/Creatify/FontFaces/OldBlue.js new file mode 100644 index 00000000..9e7a2b61 --- /dev/null +++ b/client/src/components/Creatify/FontFaces/OldBlue.js @@ -0,0 +1,36 @@ +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 id = `curve-${text.replace(/[^A-Za-z0-9]/g, '')}-oceanwave` + return ( + + + + + {text} + + + + + {text} + + + + ); + }, +}; From dff7d8877f4fc434b4567ac463023d341be1b279 Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 15 Nov 2018 02:24:08 -0600 Subject: [PATCH 14/16] Tweak OldBlue font --- client/src/components/Creatify/FontFaces/OldBlue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Creatify/FontFaces/OldBlue.js b/client/src/components/Creatify/FontFaces/OldBlue.js index 9e7a2b61..c84cd629 100644 --- a/client/src/components/Creatify/FontFaces/OldBlue.js +++ b/client/src/components/Creatify/FontFaces/OldBlue.js @@ -25,7 +25,7 @@ module.exports = { {text} - + {text} From adfcbfdbeab266c73324222211abce7ed82c5082 Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 29 Nov 2018 20:25:49 -0600 Subject: [PATCH 15/16] Add fonts (Lazer, Green Machine, Inferno), fix fiont previews, add save message --- client/scss/_dropzone.scss | 6 +++ .../Creatify/EditableFontface/index.js | 43 +++++++++++++++---- .../Creatify/FontFaces/GreenMachine.js | 6 ++- .../components/Creatify/FontFaces/Inferno.js | 22 ++++++++++ .../components/Creatify/FontFaces/Lazer.js | 27 ++++++++++++ .../components/Creatify/FontFaces/OldBlue.js | 3 ++ .../Creatify/FontFaces/VaporWave.js | 6 +++ .../Creatify/RichDraggable/index.js | 6 +-- client/src/components/Creatify/index.js | 4 +- client/src/containers/Dropzone/view.jsx | 7 +-- 10 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 client/src/components/Creatify/FontFaces/Inferno.js create mode 100644 client/src/components/Creatify/FontFaces/Lazer.js diff --git a/client/scss/_dropzone.scss b/client/scss/_dropzone.scss index 3c5feb35..a3934edd 100644 --- a/client/scss/_dropzone.scss +++ b/client/scss/_dropzone.scss @@ -73,6 +73,12 @@ z-index: 3; } +.dropzone-memeify-saveMessage { + padding-top: .25em; + position: relative; + top: .5em; +} + .dropzone-memeify-toolbar { /* TODO: Cleanup `!important` */ background: $primary-color !important; diff --git a/client/src/components/Creatify/EditableFontface/index.js b/client/src/components/Creatify/EditableFontface/index.js index d2f0e18d..3c0da159 100644 --- a/client/src/components/Creatify/EditableFontface/index.js +++ b/client/src/components/Creatify/EditableFontface/index.js @@ -7,32 +7,48 @@ export default class EditableFontface extends Component { super(props); this.state = { + blinkSelection: props.blinkSelection == false ? false : true, value: props.value, }; + + this.textInput = React.createRef(); + } + + componentDidMount() { + const textInput = this.textInput.current; + + if(textInput) { + textInput.focus(); + } } render() { const me = this; const { + blinkSelection, value } = me.state; const { editable = true, fontFace, + preview, } = me.props; const textRender = fontFace.textRender || DEFAULT_TEXT_RENDER; const textStyles = Object.assign({ + ...(blinkSelection ? { + animation: 'textBlink 1s infinite', + } : {}), minHeight: '20px', WebkitFontSmoothing: 'antialiased', MozOsxFontSmoothing: 'grayscale', - }, fontFace.text); + }, fontFace.text, preview ? fontFace.previewOverrides : {}); const fontInput = (editable === true) ? ( - me.onKeyPress(e)} onChange={(e) => me.onChange(e)} style={{ + me.onKeyPress(e)} onChange={(e) => me.onChange(e)} style={{ ...{ bottom: 0, opacity: 0, @@ -49,25 +65,34 @@ export default class EditableFontface extends Component { return (
- {fontInput} -
{textRender(value)}
+ + {fontInput} +
{textRender(value)}
); } onKeyPress(e) { - this.setState({ value: e.target.value }); + this.setState({ + blinkSelection: false, + value: e.target.value + }); } onChange(e) { - this.setState({ value: e.target.value }); + this.setState({ + blinkSelection: false, + value: e.target.value + }); } }; export const PRESETS = { - 'Retro Rainbow': require('../FontFaces/RetroRainbow'), + 'Inferno': require('../FontFaces/Inferno'), 'Green Machine': require('../FontFaces/GreenMachine'), - 'vapor wave': require('../FontFaces/VaporWave'), - 'The Special': require('../FontFaces/TheSpecial'), + 'Lazer': require('../FontFaces/Lazer'), 'Old Blue': require('../FontFaces/OldBlue'), + 'Retro Rainbow': require('../FontFaces/RetroRainbow'), + 'The Special': require('../FontFaces/TheSpecial'), + 'Vapor Wave': require('../FontFaces/VaporWave'), } diff --git a/client/src/components/Creatify/FontFaces/GreenMachine.js b/client/src/components/Creatify/FontFaces/GreenMachine.js index b3163983..071a1e2c 100644 --- a/client/src/components/Creatify/FontFaces/GreenMachine.js +++ b/client/src/components/Creatify/FontFaces/GreenMachine.js @@ -1,6 +1,10 @@ module.exports = { container: {}, text: { - color: 'green', + color: '#00b700', + fontFamily: 'courier, Courier New', + fontSize: '2rem', + fontWeight: 'bold', + textShadow: '1px 1px 2px #003605', }, }; diff --git a/client/src/components/Creatify/FontFaces/Inferno.js b/client/src/components/Creatify/FontFaces/Inferno.js new file mode 100644 index 00000000..01d4d9cb --- /dev/null +++ b/client/src/components/Creatify/FontFaces/Inferno.js @@ -0,0 +1,22 @@ +module.exports = { + container: {}, + editorStyle: { + fontFamily: 'helvetica, Helvetica Nue', + fontWeight: 'bold', + fontSize: '2em', + padding: '4rem 2rem 1rem 2rem', + }, + text: { + fontFamily: 'helvetica, Helvetica Nue', + fontWeight: 'bold', + fontSize: '2em', + color: '#fff', + paddingBottom: '.25em', + padding: '4rem 2rem 1rem 2rem', + textShadow: '0px 0px 3px #c20000, 0px 0px 3px #e0f2ff, 0px 0px 5px #532761, 0px 0px 20px #670606, 0 0 10px rgba(0, 0, 0, .8), 0 0 10px #fefcc9, 5px -5px 15px #feec85, -10px -10px 20px #ffae34, 10px -20px 25px #ec760c, -10px -30px 30px #cd4606, 0 -40px 35px #973716, 5px -45px 40px #451b0e, 0 -2px 15px rgba(255, 200, 160, .5)', + }, + previewOverrides: { + fontSize: '1.5em', + padding: '0 1rem 0 1rem', + }, +}; diff --git a/client/src/components/Creatify/FontFaces/Lazer.js b/client/src/components/Creatify/FontFaces/Lazer.js new file mode 100644 index 00000000..ffd2747b --- /dev/null +++ b/client/src/components/Creatify/FontFaces/Lazer.js @@ -0,0 +1,27 @@ +module.exports = { + container: {}, + editorStyle: { + fontFamily: 'helvetica, Helvetica Nue', + fontWeight: 'bold', + fontSize: '2em', + padding: '.05rem', + textTransform: 'uppercase', + whiteSpace: 'nowrap', + }, + text: { + fontFamily: 'helvetica, Helvetica Nue', + fontWeight: 'bold', + backgroundImage: 'linear-gradient(180deg, #249bff 0%, #e1f8ff 44%, #3a006b 44%, #ff57d6 100%)', + backgroundClip: 'text', + fontSize: '2em', + color: 'transparent', + paddingBottom: '.25em', + paddingTop: '.1em', + filter: 'drop-shadow(0 0 .05rem black)', + padding: '.05rem', + textTransform: 'uppercase', + whiteSpace: 'nowrap', + WebkitBackgroundClip: 'text', + WebkitTextStroke: '0.03em rgba(255, 255, 255, 0.6)', + }, +}; diff --git a/client/src/components/Creatify/FontFaces/OldBlue.js b/client/src/components/Creatify/FontFaces/OldBlue.js index c84cd629..99448d87 100644 --- a/client/src/components/Creatify/FontFaces/OldBlue.js +++ b/client/src/components/Creatify/FontFaces/OldBlue.js @@ -15,6 +15,9 @@ module.exports = { text: { fontFamily: 'Segoe UI,Helvetica,Arial', }, + previewOverrides: { + height: '2.6rem', + }, textRender: (text) => { const id = `curve-${text.replace(/[^A-Za-z0-9]/g, '')}-oceanwave` return ( diff --git a/client/src/components/Creatify/FontFaces/VaporWave.js b/client/src/components/Creatify/FontFaces/VaporWave.js index f45f2897..7d2f0575 100644 --- a/client/src/components/Creatify/FontFaces/VaporWave.js +++ b/client/src/components/Creatify/FontFaces/VaporWave.js @@ -15,6 +15,12 @@ module.exports = { text: { fontFamily: 'Segoe UI,Helvetica,Arial', }, + previewOverrides: { + transform: 'rotate(39deg)', + height: '7rem', + paddingLeft: '2rem', + margin: '-2rem 0', + }, textRender: (text) => { const formattedText = text.toLowerCase().split('').map((char) => { const c = char.charCodeAt( 0 ) diff --git a/client/src/components/Creatify/RichDraggable/index.js b/client/src/components/Creatify/RichDraggable/index.js index 8ec02ede..8b9915c3 100644 --- a/client/src/components/Creatify/RichDraggable/index.js +++ b/client/src/components/Creatify/RichDraggable/index.js @@ -41,15 +41,15 @@ export default class RichDraggable extends Component { } = props.bounds; const bounds = { - top: 0, - left: 0, + //top: 0, + //left: 0, right: right - state.width, bottom: bottom - state.height, }; return ( -
+
{props.children}
diff --git a/client/src/components/Creatify/index.js b/client/src/components/Creatify/index.js index acae72f6..91c77c9e 100644 --- a/client/src/components/Creatify/index.js +++ b/client/src/components/Creatify/index.js @@ -95,7 +95,7 @@ export default class Creatify extends Component { value: fontName, label: (
- +
), fontName, @@ -140,7 +140,7 @@ export default class Creatify extends Component { const newElement = ( this.setActiveElement(newElement)}> - + ); diff --git a/client/src/containers/Dropzone/view.jsx b/client/src/containers/Dropzone/view.jsx index db9bdc66..f7f05a95 100644 --- a/client/src/containers/Dropzone/view.jsx +++ b/client/src/containers/Dropzone/view.jsx @@ -130,7 +130,7 @@ class Dropzone extends React.Component { const destinationFormat = 'image/jpeg'; canvas.toBlob((blob) => { - const file = new File([blob], 'memeify.jpg', { + const file = new File([blob], `memeify-${Math.random().toString(36).substring(7)}.jpg`, { type: destinationFormat, }); @@ -172,7 +172,7 @@ class Dropzone extends React.Component { const memeifyContent = memeify && file && filePreview ? ( this.selectFileFromCanvas(canvas)}> -
+
@@ -186,7 +186,7 @@ class Dropzone extends React.Component {

Video updates are currently disabled. This feature will be available soon. You can edit metadata.

) : (
- { hasContent && !memeify && ( + { hasContent && !memeify && fileExt !== 'mp4' && (
this.setState({ memeify: !memeify })}> Memeify
@@ -222,6 +222,7 @@ class Dropzone extends React.Component { ) )} + {memeifyContent ?
{`Don't forget to save before you publish.`}
: null}
)} From 7b53cf48ec510b23b5fc753eac7315bd5fc454c5 Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 29 Nov 2018 20:54:24 -0600 Subject: [PATCH 16/16] Font tweaks and add Neon font --- .../Creatify/EditableFontface/index.js | 3 ++- .../Creatify/FontFaces/GreenMachine.js | 5 +++++ .../components/Creatify/FontFaces/Inferno.js | 3 --- .../components/Creatify/FontFaces/Lazer.js | 4 ---- .../src/components/Creatify/FontFaces/Neon.js | 21 +++++++++++++++++++ .../Creatify/RichDraggable/index.js | 2 +- 6 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 client/src/components/Creatify/FontFaces/Neon.js diff --git a/client/src/components/Creatify/EditableFontface/index.js b/client/src/components/Creatify/EditableFontface/index.js index 3c0da159..2ef131e0 100644 --- a/client/src/components/Creatify/EditableFontface/index.js +++ b/client/src/components/Creatify/EditableFontface/index.js @@ -88,9 +88,10 @@ export default class EditableFontface extends Component { }; export const PRESETS = { - 'Inferno': require('../FontFaces/Inferno'), 'Green Machine': require('../FontFaces/GreenMachine'), + 'Inferno': require('../FontFaces/Inferno'), 'Lazer': require('../FontFaces/Lazer'), + 'Neon': require('../FontFaces/Neon'), 'Old Blue': require('../FontFaces/OldBlue'), 'Retro Rainbow': require('../FontFaces/RetroRainbow'), 'The Special': require('../FontFaces/TheSpecial'), diff --git a/client/src/components/Creatify/FontFaces/GreenMachine.js b/client/src/components/Creatify/FontFaces/GreenMachine.js index 071a1e2c..60b0b462 100644 --- a/client/src/components/Creatify/FontFaces/GreenMachine.js +++ b/client/src/components/Creatify/FontFaces/GreenMachine.js @@ -1,5 +1,10 @@ module.exports = { container: {}, + editorStyle: { + fontFamily: 'courier, Courier New', + fontWeight: 'bold', + fontSize: '2em', + }, text: { color: '#00b700', fontFamily: 'courier, Courier New', diff --git a/client/src/components/Creatify/FontFaces/Inferno.js b/client/src/components/Creatify/FontFaces/Inferno.js index 01d4d9cb..45fc4b20 100644 --- a/client/src/components/Creatify/FontFaces/Inferno.js +++ b/client/src/components/Creatify/FontFaces/Inferno.js @@ -4,15 +4,12 @@ module.exports = { fontFamily: 'helvetica, Helvetica Nue', fontWeight: 'bold', fontSize: '2em', - padding: '4rem 2rem 1rem 2rem', }, text: { fontFamily: 'helvetica, Helvetica Nue', fontWeight: 'bold', fontSize: '2em', color: '#fff', - paddingBottom: '.25em', - padding: '4rem 2rem 1rem 2rem', textShadow: '0px 0px 3px #c20000, 0px 0px 3px #e0f2ff, 0px 0px 5px #532761, 0px 0px 20px #670606, 0 0 10px rgba(0, 0, 0, .8), 0 0 10px #fefcc9, 5px -5px 15px #feec85, -10px -10px 20px #ffae34, 10px -20px 25px #ec760c, -10px -30px 30px #cd4606, 0 -40px 35px #973716, 5px -45px 40px #451b0e, 0 -2px 15px rgba(255, 200, 160, .5)', }, previewOverrides: { diff --git a/client/src/components/Creatify/FontFaces/Lazer.js b/client/src/components/Creatify/FontFaces/Lazer.js index ffd2747b..96373b4c 100644 --- a/client/src/components/Creatify/FontFaces/Lazer.js +++ b/client/src/components/Creatify/FontFaces/Lazer.js @@ -4,7 +4,6 @@ module.exports = { fontFamily: 'helvetica, Helvetica Nue', fontWeight: 'bold', fontSize: '2em', - padding: '.05rem', textTransform: 'uppercase', whiteSpace: 'nowrap', }, @@ -15,10 +14,7 @@ module.exports = { backgroundClip: 'text', fontSize: '2em', color: 'transparent', - paddingBottom: '.25em', - paddingTop: '.1em', filter: 'drop-shadow(0 0 .05rem black)', - padding: '.05rem', textTransform: 'uppercase', whiteSpace: 'nowrap', WebkitBackgroundClip: 'text', diff --git a/client/src/components/Creatify/FontFaces/Neon.js b/client/src/components/Creatify/FontFaces/Neon.js new file mode 100644 index 00000000..16d0eb20 --- /dev/null +++ b/client/src/components/Creatify/FontFaces/Neon.js @@ -0,0 +1,21 @@ +module.exports = { + container: {}, + editorStyle: { + fontFamily: 'Helvetica, Arial', + fontWeight: 'bold', + fontSize: '2em', + letterSpacing: '.1em', + }, + text: { + color: '#fff', + fontFamily: 'Helvetica, Arial', + fontSize: '2rem', + fontWeight: 'bold', + letterSpacing: '.1em', + textShadow: '0 0 0.05em #fff, 0 0 0.1em #fff, 0 0 0.2em #fff, 0 0 .2em #ff1de2, 0 0 .4em #ff26e3, 0 0 .5em #ff00de, 0 0 1em #ff61eb, 0 0 1.5em #ff7cee', + }, + previewOverrides: { + fontSize: '1.8em', + padding: '0 1rem 0 1rem', + }, +}; diff --git a/client/src/components/Creatify/RichDraggable/index.js b/client/src/components/Creatify/RichDraggable/index.js index 8b9915c3..c0e9e462 100644 --- a/client/src/components/Creatify/RichDraggable/index.js +++ b/client/src/components/Creatify/RichDraggable/index.js @@ -50,7 +50,7 @@ export default class RichDraggable extends Component { return (
-
+
{props.children}