make font face updatable and add google fonts

This commit is contained in:
casperlamboo 2018-01-09 15:42:25 +01:00
parent 01bd55796e
commit b42c7829ca
15 changed files with 193 additions and 29 deletions

BIN
img/contextmenu/btnFont.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

54
package-lock.json generated
View File

@ -1932,6 +1932,11 @@
"isarray": "1.0.0" "isarray": "1.0.0"
} }
}, },
"buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
},
"buffer-from": { "buffer-from": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.1.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.1.tgz",
@ -4286,6 +4291,14 @@
} }
} }
}, },
"fd-slicer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
"integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
"requires": {
"pend": "1.2.0"
}
},
"figures": { "figures": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
@ -5666,6 +5679,33 @@
"pinkie-promise": "2.0.1" "pinkie-promise": "2.0.1"
} }
}, },
"google-fonts-webpack-plugin": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/google-fonts-webpack-plugin/-/google-fonts-webpack-plugin-0.4.4.tgz",
"integrity": "sha512-+e2D9/DVBG9EDydRovzoqMZ658SsTBGbC0c65GyZqkwNvdj8vRSYQKXqbz7/yt7QaXsCPT1MpH45r3ivWOitcw==",
"requires": {
"lodash": "4.17.4",
"node-fetch": "1.7.3",
"webpack-sources": "0.2.3",
"yauzl": "2.9.1"
},
"dependencies": {
"source-list-map": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-1.1.2.tgz",
"integrity": "sha1-mIkBnRAkzOVc3AaUmDN+9hhqEaE="
},
"webpack-sources": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz",
"integrity": "sha1-F8Yr+vE8cH+dAsR54Nzd6DgGl/s=",
"requires": {
"source-list-map": "1.1.2",
"source-map": "0.5.7"
}
}
}
},
"got": { "got": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/got/-/got-3.3.1.tgz", "resolved": "https://registry.npmjs.org/got/-/got-3.3.1.tgz",
@ -10550,6 +10590,11 @@
"integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=", "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=",
"dev": true "dev": true
}, },
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
},
"pepjs": { "pepjs": {
"version": "0.4.3", "version": "0.4.3",
"resolved": "https://registry.npmjs.org/pepjs/-/pepjs-0.4.3.tgz", "resolved": "https://registry.npmjs.org/pepjs/-/pepjs-0.4.3.tgz",
@ -14364,6 +14409,15 @@
} }
} }
}, },
"yauzl": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.1.tgz",
"integrity": "sha1-qBmB6nCleUYTOIPwKcWCGok1mn8=",
"requires": {
"buffer-crc32": "0.2.13",
"fd-slicer": "1.0.1"
}
},
"yml-loader": { "yml-loader": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/yml-loader/-/yml-loader-2.1.0.tgz", "resolved": "https://registry.npmjs.org/yml-loader/-/yml-loader-2.1.0.tgz",

View File

@ -27,6 +27,7 @@
"blueimp-canvas-to-blob": "^3.14.0", "blueimp-canvas-to-blob": "^3.14.0",
"bowser": "^1.8.1", "bowser": "^1.8.1",
"fit-curve": "^0.1.6", "fit-curve": "^0.1.6",
"google-fonts-webpack-plugin": "^0.4.4",
"imports-loader": "^0.7.1", "imports-loader": "^0.7.1",
"jss": "^9.4.0", "jss": "^9.4.0",
"keycode": "^2.1.9", "keycode": "^2.1.9",

View File

@ -390,8 +390,8 @@ export function d2textInit(position, textId, screenMatrixContainer, screenMatrix
dispatch({ type: D2_TEXT_INIT, position, textId, screenMatrixContainer, screenMatrixZoom }); dispatch({ type: D2_TEXT_INIT, position, textId, screenMatrixContainer, screenMatrixZoom });
}; };
} }
export function d2textInputChange(text, family, weight, style, fill) { export function d2textInputChange(text) {
return { type: D2_TEXT_INPUT_CHANGE, text, family, weight, style, fill }; return { type: D2_TEXT_INPUT_CHANGE, text };
} }
const traceDragThrottle = createThrottle(); const traceDragThrottle = createThrottle();

View File

@ -37,10 +37,9 @@ class InputText extends React.Component {
if (!shapeData) return; if (!shapeData) return;
const { changeText } = this.props; const { changeText } = this.props;
const { family, weight, style } = shapeData.text;
const text = this.refs.text.value; const text = this.refs.text.value;
changeText(text, family, weight, style, true); changeText(text);
}; };
getShapeData = () => { getShapeData = () => {
@ -71,7 +70,7 @@ class InputText extends React.Component {
> >
<input <input
className={classes.textInput} className={classes.textInput}
style={{ family: shapeData.text.family }} style={{ fontFamily: shapeData.text.family }}
value={shapeData.text.text} value={shapeData.text.text}
ref="text" ref="text"
spellCheck="false" spellCheck="false"

View File

@ -9,6 +9,8 @@ import * as contextTools from '../constants/contextTools.js';
import * as d2Tools from '../constants/d2Tools.js'; import * as d2Tools from '../constants/d2Tools.js';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import injectSheet from 'react-jss'; import injectSheet from 'react-jss';
import { FONT_TOOLS } from '../constants/contextTools.js';
import { FONT_FACE } from '../constants/general.js';
// TODO move this to jss instead of css // TODO move this to jss instead of css
import '../../styles/styles.css'; import '../../styles/styles.css';
// import createDebug from 'debug'; // import createDebug from 'debug';
@ -120,6 +122,7 @@ function renderChildren(children) {
for (const child of children) { for (const child of children) {
let component; let component;
if (child.children.length > 0) { if (child.children.length > 0) {
component = ( component = (
<SubMenu <SubMenu
@ -135,6 +138,18 @@ function renderChildren(children) {
{renderChildren(child.children)} {renderChildren(child.children)}
</SubMenu> </SubMenu>
); );
} else if (FONT_TOOLS.includes(child.value)) {
component = (
<MenuItem
id={child.value}
value={child.value}
key={child.value}
svg={child.svg}
disabled={child.disabled}
>
<p style={{ fontFamily: FONT_FACE[child.value] }}>{FONT_FACE[child.value]}</p>
</MenuItem>
);
} else { } else {
component = ( component = (
<MenuItem <MenuItem
@ -151,7 +166,7 @@ function renderChildren(children) {
return components; return components;
} }
function filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, menus) { function filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, selectionIncludesText, menus) {
const showUnion = activeTool === d2Tools.TRANSFORM && numFilledObjects && numSelectedObjects >= 2; const showUnion = activeTool === d2Tools.TRANSFORM && numFilledObjects && numSelectedObjects >= 2;
const showIntersect = activeTool === d2Tools.TRANSFORM && numSelectedObjects >= 1; const showIntersect = activeTool === d2Tools.TRANSFORM && numSelectedObjects >= 1;
return { return {
@ -191,11 +206,14 @@ function filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidO
case contextTools.HOLE_TOGGLE: case contextTools.HOLE_TOGGLE:
return numSelectedObjects > 0; return numSelectedObjects > 0;
case contextTools.FONT:
return selectionIncludesText || activeTool === d2Tools.TEXT;
default: default:
return true; return true;
} }
}).map(child => { }).map(child => {
return filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, child); return filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, selectionIncludesText, child);
}) })
}; };
} }
@ -217,11 +235,12 @@ const getMenus = createSelector([
state => state.sketcher.present.d2.tool, state => state.sketcher.present.d2.tool,
state => state.sketcher.present.selection.objects.length, state => state.sketcher.present.selection.objects.length,
state => state.sketcher.present.selection.objects.filter(({ id }) => state.sketcher.present.objectsById[id].fill).length, state => state.sketcher.present.selection.objects.filter(({ id }) => state.sketcher.present.objectsById[id].fill).length,
state => state.sketcher.present.selection.objects.filter(({ id }) => state.sketcher.present.objectsById[id].solid).length state => state.sketcher.present.selection.objects.filter(({ id }) => state.sketcher.present.objectsById[id].solid).length,
], (menus, activeTool, numSelectedObjects, numFilledObjects, numSolidObjects) => ({ state => state.sketcher.present.selection.objects.some(({ id }) => state.sketcher.present.objectsById[id].type === 'TEXT')
toolbar2d: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, nestChildren(menus, menus[TOOLBAR2D])), ], (menus, activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, selectionIncludesText) => ({
toolbar3d: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, nestChildren(menus, menus[TOOLBAR3D])), toolbar2d: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, selectionIncludesText, nestChildren(menus, menus[TOOLBAR2D])),
context: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, nestChildren(menus, menus[CONTEXT])) toolbar3d: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, selectionIncludesText, nestChildren(menus, menus[TOOLBAR3D])),
context: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, selectionIncludesText, nestChildren(menus, menus[CONTEXT]))
})); }));
export default injectSheet(styles)(connect(getMenus)(SketcherToolbars)); export default injectSheet(styles)(connect(getMenus)(SketcherToolbars));

View File

@ -8,6 +8,7 @@ export const HOLE_TOGGLE = 'hole-toggle-tool';
export const ALIGN = 'align-tool'; export const ALIGN = 'align-tool';
export const UNION = 'union-tool'; export const UNION = 'union-tool';
export const INTERSECT = 'intersect-tool'; export const INTERSECT = 'intersect-tool';
export const FONT = 'font-tool';
export const LIGHT_BLUE_A = 'color-light-blue-a'; export const LIGHT_BLUE_A = 'color-light-blue-a';
export const LIGHT_BLUE_B = 'color-light-blue-b'; export const LIGHT_BLUE_B = 'color-light-blue-b';
@ -79,3 +80,23 @@ export const ALIGN_TOOLS = [
ALIGN_VERTICAL, ALIGN_VERTICAL,
ALIGN_BOTTOM ALIGN_BOTTOM
]; ];
export const OSWALD = 'oswald';
export const RANGA = 'ranga';
export const JOTI_ONE = 'joti-one';
export const BELLEFAIR = 'bellefair';
export const LOBSTER = 'lobster';
export const ABRIL_FATFACE = 'abril-fatface';
export const PLAY = 'play';
export const FASCINATE = 'fascinate';
export const FONT_TOOLS = [
OSWALD,
RANGA,
JOTI_ONE,
BELLEFAIR,
LOBSTER,
ABRIL_FATFACE,
PLAY,
FASCINATE
];

View File

@ -37,3 +37,14 @@ export const COLOR_STRING_TO_HEX = {
[contextTools.BLACK_B]: 0xAAAAAA, [contextTools.BLACK_B]: 0xAAAAAA,
[contextTools.BLACK_C]: 0x444444 [contextTools.BLACK_C]: 0x444444
}; };
export const FONT_FACE = {
[contextTools.OSWALD]: 'Oswald',
[contextTools.RANGA]: 'Ranga',
[contextTools.JOTI_ONE]: 'Joti One',
[contextTools.BELLEFAIR]: 'Bellefair',
[contextTools.LOBSTER]: 'Lobster',
[contextTools.ABRIL_FATFACE]: 'Abril Fatface',
[contextTools.PLAY]: 'Play',
[contextTools.FASCINATE]: 'Fascinate'
};

View File

@ -109,6 +109,11 @@ const context = {
selected: contextTools.ALIGN_HORIZONTAL, selected: contextTools.ALIGN_HORIZONTAL,
children: contextTools.ALIGN_TOOLS.map(value => ({ value })), children: contextTools.ALIGN_TOOLS.map(value => ({ value })),
...selectorBehavior ...selectorBehavior
}, {
value: contextTools.FONT,
selected: contextTools.OSWALD,
children: contextTools.FONT_TOOLS.map(value => ({ value })),
...selectorBehavior
}, },
{ value: contextTools.UNION }, { value: contextTools.UNION },
{ value: contextTools.INTERSECT } { value: contextTools.INTERSECT }

View File

@ -1,6 +1,8 @@
import { Vector, Matrix } from '@doodle3d/cal'; import { Vector, Matrix } from '@doodle3d/cal';
import * as d2Tools from './d2Tools'; import * as d2Tools from './d2Tools';
import * as d3Tools from './d3Tools'; import * as d3Tools from './d3Tools';
import * as contextTools from './contextTools';
import { FONT_FACE } from '../constants/general.js';
const SHAPE = { const SHAPE = {
D3Visible: true, D3Visible: true,
@ -58,7 +60,7 @@ export const SHAPE_TYPE_PROPERTIES = {
...SHAPE, ...SHAPE,
defaultProperties: { defaultProperties: {
...defaultProperties, ...defaultProperties,
text: { text: '', family: 'Arial', weight: 'normal', style: 'normal' }, text: { text: '', family: FONT_FACE[contextTools.OSWALD], weight: 'normal', style: 'normal' },
fill: true fill: true
} }
}, },

View File

@ -1,6 +1,6 @@
import update from 'react-addons-update'; import update from 'react-addons-update';
import * as contextTools from '../constants/contextTools.js'; import * as contextTools from '../constants/contextTools.js';
import { COLOR_STRING_TO_HEX } from '../constants/general.js'; import { COLOR_STRING_TO_HEX, FONT_FACE } from '../constants/general.js';
import { ERASER_SIZES, BRUSH_SIZES } from '../constants/d2Constants.js'; import { ERASER_SIZES, BRUSH_SIZES } from '../constants/d2Constants.js';
import { SHAPE_TYPE_PROPERTIES } from '../constants/shapeTypeProperties.js'; import { SHAPE_TYPE_PROPERTIES } from '../constants/shapeTypeProperties.js';
import * as actions from '../actions/index.js'; import * as actions from '../actions/index.js';
@ -69,6 +69,33 @@ export default function (state, action) {
}); });
} }
case contextTools.OSWALD:
case contextTools.RANGA:
case contextTools.JOTI_ONE:
case contextTools.BELLEFAIR:
case contextTools.LOBSTER:
case contextTools.ABRIL_FATFACE:
case contextTools.PLAY:
case contextTools.FASCINATE: {
const family = FONT_FACE[action.tool];
const { activeShape } = state.d2;
if (activeShape && state.objectsById[activeShape].type === 'TEXT') {
state = update(state, { objectsById: { [activeShape]: { text: { family: { $set: family } } } } });
}
return update(state, {
objectsById: state.selection.objects.reduce((updateObject, { id }) => {
if (state.objectsById[id].type === 'TEXT') {
updateObject[id] = { text: { family: { $set: FONT_FACE[action.tool] } } };
}
return updateObject;
}, {}),
context: {
font: { $set: FONT_FACE[action.tool] }
}
});
}
case contextTools.LIGHT_BLUE_A: case contextTools.LIGHT_BLUE_A:
case contextTools.LIGHT_BLUE_B: case contextTools.LIGHT_BLUE_B:
case contextTools.LIGHT_BLUE_C: case contextTools.LIGHT_BLUE_C:
@ -94,6 +121,11 @@ export default function (state, action) {
case contextTools.BLACK_B: case contextTools.BLACK_B:
case contextTools.BLACK_C: { case contextTools.BLACK_C: {
const color = COLOR_STRING_TO_HEX[action.tool]; const color = COLOR_STRING_TO_HEX[action.tool];
const { activeShape } = state.d2;
if (activeShape) {
state = update(state, { objectsById: { [activeShape]: { color: { $set: color } } } });
}
return updateColor(state, color); return updateColor(state, color);
} }

View File

@ -8,7 +8,6 @@ const debug = createDebug('d3d:reducer:text');
export default function textReducer(state, action) { export default function textReducer(state, action) {
if (action.log !== false) debug(action.type); if (action.log !== false) debug(action.type);
switch (action.type) { switch (action.type) {
case actions.D2_TEXT_INIT: { case actions.D2_TEXT_INIT: {
state = removeEmptyText(state); state = removeEmptyText(state);
@ -23,24 +22,26 @@ export default function textReducer(state, action) {
} else { } else {
return addObjectActive2D(state, { return addObjectActive2D(state, {
transform: new Matrix({ x: screenPosition.x, y: screenPosition.y }), transform: new Matrix({ x: screenPosition.x, y: screenPosition.y }),
type: 'TEXT' type: 'TEXT',
text: {
text: '',
family: state.context.font,
weight: 'normal',
style: 'normal'
}
}); });
} }
return state; return state;
} }
case actions.D2_TEXT_INPUT_CHANGE: { case actions.D2_TEXT_INPUT_CHANGE: {
const { text, family, style, weight, fill } = action; const { text } = action;
const { activeShape } = state.d2; const { activeShape } = state.d2;
return update(state, { return update(state, {
objectsById: { objectsById: {
[activeShape]: { [activeShape]: {
text: { text: {
text: { $set: text }, text: { $set: text }
family: { $set: family }, }
style: { $set: style },
weight: { $set: weight }
},
fill: { $set: fill }
} }
} }
}); });

View File

@ -4,7 +4,7 @@ import undoFilter from '../utils/undoFilter.js';
import * as actions from '../actions/index.js'; import * as actions from '../actions/index.js';
import * as d2Tools from '../constants/d2Tools.js'; import * as d2Tools from '../constants/d2Tools.js';
import * as d3Tools from '../constants/d3Tools.js'; import * as d3Tools from '../constants/d3Tools.js';
import { COLOR_STRING_TO_HEX } from '../constants/general.js'; import { COLOR_STRING_TO_HEX, FONT_FACE } from '../constants/general.js';
import * as contextTools from '../constants/contextTools.js'; import * as contextTools from '../constants/contextTools.js';
import { ERASER_SIZES, BRUSH_SIZES } from '../constants/d2Constants.js'; import { ERASER_SIZES, BRUSH_SIZES } from '../constants/d2Constants.js';
import update from 'react-addons-update'; import update from 'react-addons-update';
@ -36,7 +36,8 @@ const initialState = {
objectIdCounter: 0, objectIdCounter: 0,
context: { context: {
solid: true, solid: true,
color: COLOR_STRING_TO_HEX[contextTools.LIGHT_BLUE_B] color: COLOR_STRING_TO_HEX[contextTools.LIGHT_BLUE_B],
font: FONT_FACE[contextTools.OSWALD]
}, },
selection: { selection: {
transform: new Matrix(), transform: new Matrix(),

View File

@ -249,6 +249,13 @@
flex-wrap: wrap; flex-wrap: wrap;
} }
#font-tool > .button {
background-image: url('../img/contextmenu/btnFont.png');
background-size: 40px auto;
width: 40px;
height: 40px;
}
#color-light-blue-a { fill: #BCFFFF; } #color-light-blue-a { fill: #BCFFFF; }
#color-light-blue-b { fill: #68E1FD; } #color-light-blue-b { fill: #68E1FD; }
#color-light-blue-c { fill: #01B8FF; } #color-light-blue-c { fill: #01B8FF; }

View File

@ -3,6 +3,7 @@ const path = require('path');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const HTMLWebpackPlugin = require('html-webpack-plugin'); const HTMLWebpackPlugin = require('html-webpack-plugin');
const CordovaPlugin = require('webpack-cordova-plugin'); const CordovaPlugin = require('webpack-cordova-plugin');
const GoogleFontsPlugin = require('google-fonts-webpack-plugin');
const devMode = process.env.NODE_ENV !== 'production'; const devMode = process.env.NODE_ENV !== 'production';
const appMode = process.env.TARGET === 'app'; const appMode = process.env.TARGET === 'app';
@ -75,9 +76,7 @@ module.exports = {
}, },
plugins: [ plugins: [
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env': { 'process.env.TARGET': JSON.stringify(process.env.TARGET)
'TARGET': JSON.stringify(process.env.TARGET)
}
}), }),
new HTMLWebpackPlugin({ new HTMLWebpackPlugin({
title: 'Doodle3D Core - Simple example', title: 'Doodle3D Core - Simple example',
@ -86,6 +85,18 @@ module.exports = {
scripts: appMode ? ['cordova.js'] : null, scripts: appMode ? ['cordova.js'] : null,
appMountId: 'app' appMountId: 'app'
}), }),
new GoogleFontsPlugin({
fonts: [
{ family: 'Oswald' },
{ family: 'Ranga' },
{ family: 'Joti One' },
{ family: 'Bellefair' },
{ family: 'Lobster' },
{ family: 'Abril Fatface' },
{ family: 'Play' },
{ family: 'Fascinate' }
]
}),
...(appMode ? [ ...(appMode ? [
new CordovaPlugin({ new CordovaPlugin({
config: 'config.xml', config: 'config.xml',
@ -95,7 +106,7 @@ module.exports = {
}) })
] : []) ] : [])
], ],
devtool: "source-map", devtool: 'source-map',
devServer: { devServer: {
contentBase: 'dist' contentBase: 'dist'
} }