stickers: drawing arrow + text

This commit is contained in:
muerwre 2018-12-06 09:20:55 +07:00
parent f183c8593d
commit 9729a9e117
11 changed files with 120 additions and 25 deletions

View file

@ -14,3 +14,5 @@ const database = mongoose.connection;
database.on('error', (err) => { console.error(`Database Connection Error: ${err}`); process.exit(2); }); database.on('error', (err) => { console.error(`Database Connection Error: ${err}`); process.exit(2); });
database.on('connected', () => { console.info('Succesfully connected to MongoDB Database'); }); database.on('connected', () => { console.info('Succesfully connected to MongoDB Database'); });
console.log(`DB: mongodb://${USER}:${PASSWORD}@${HOSTNAME}:${PORT}/${DATABASE}`);

View file

@ -8,8 +8,8 @@ const RouteSchema = new Schema(
title: { type: String, default: '' }, title: { type: String, default: '' },
// address: { type: String, required: true }, // address: { type: String, required: true },
version: { type: Number, default: 2 }, version: { type: Number, default: 2 },
route: { type: Array }, route: { type: Array, default: [] },
stickers: { type: Array }, stickers: { type: Array, default: [] },
owner: { type: Schema.Types.ObjectId, ref: 'User' }, owner: { type: Schema.Types.ObjectId, ref: 'User' },
logo: { type: String, default: 'DEFAULT' }, logo: { type: String, default: 'DEFAULT' },
distance: { type: Number, default: 0 }, distance: { type: Number, default: 0 },

View file

@ -8,7 +8,7 @@ module.exports = async (req, res) => {
path: 'routes', path: 'routes',
options: { options: {
limit: 100, limit: 100,
sort: { updated: 1 }, sort: { updated_at: -1 },
} }
}); });
const random_url = await generateRandomUrl(); const random_url = await generateRandomUrl();

View file

@ -9,7 +9,7 @@ module.exports.parseRoute = route => route.filter(el => (
)); ));
module.exports.parseStickers = stickers => stickers.filter(el => ( module.exports.parseStickers = stickers => stickers.filter(el => (
Object.keys(el).length === 4 Object.keys(el).length === 5
&& el.latlng && el.latlng
&& Object.keys(el.latlng).length === 2 && Object.keys(el.latlng).length === 2
&& el.latlng.lat && el.latlng.lat

View file

@ -30,7 +30,7 @@ export const RouteRow = ({
<div className="route-description"> <div className="route-description">
<span> <span>
<Icon icon="icon-cycle-1" /> <Icon icon="icon-cycle-1" />
{(distance && `${distance} km`) || 'N/A'} {(distance && `${distance} km`) || '0 km'}
</span> </span>
</div> </div>

View file

@ -18,6 +18,7 @@ import {
setTitle, setTitle,
} from '$redux/user/actions'; } from '$redux/user/actions';
import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers'; import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers';
import { STICKERS } from '$constants/stickers';
export class Editor { export class Editor {
constructor() { constructor() {
@ -136,7 +137,7 @@ export class Editor {
if (!e || !e.latlng || !this.activeSticker) return; if (!e || !e.latlng || !this.activeSticker) return;
const { latlng } = e; const { latlng } = e;
this.stickers.createSticker({ latlng, sticker: this.activeSticker }); this.stickers.createSticker({ latlng, sticker: this.activeSticker.sticker, set: this.activeSticker.set });
this.setActiveSticker(null); this.setActiveSticker(null);
this.setChanged(true); this.setChanged(true);
}; };
@ -232,12 +233,18 @@ export class Editor {
this.stickers.clearAll(); this.stickers.clearAll();
if (stickers) { if (stickers) {
stickers.map(sticker => this.stickers.createSticker({ stickers.map(sticker =>
sticker.set && STICKERS[sticker.set].url &&
this.stickers.createSticker({
latlng: sticker.latlng, latlng: sticker.latlng,
angle: parseStickerAngle({ sticker, version }), // angle: parseStickerAngle({ sticker, version }),
sticker: parseStickerStyle({ sticker, version }), // sticker: parseStickerStyle({ sticker, version }),
angle: sticker.angle,
sticker: sticker.sticker,
set: sticker.set,
text: sticker.text, text: sticker.text,
})); })
);
} }
if (owner) this.owner = owner; if (owner) this.owner = owner;

View file

@ -10,7 +10,7 @@ import classnames from 'classnames';
export class Sticker { export class Sticker {
constructor({ constructor({
latlng, deleteSticker, map, lockMapClicks, sticker, triggerOnChange, angle = 2.2, text = '', latlng, deleteSticker, map, lockMapClicks, sticker, set, triggerOnChange, angle = 2.2, text = '',
}) { }) {
this.text = text; this.text = text;
this.latlng = latlng; this.latlng = latlng;
@ -18,6 +18,7 @@ export class Sticker {
this.isDragging = false; this.isDragging = false;
this.map = map; this.map = map;
this.sticker = sticker; this.sticker = sticker;
this.set = set;
this.editable = true; this.editable = true;
this.triggerOnChange = triggerOnChange; this.triggerOnChange = triggerOnChange;
this.direction = 'right'; this.direction = 'right';
@ -38,7 +39,7 @@ export class Sticker {
onMouseUp={this.onDragStop} onMouseUp={this.onDragStop}
> >
<StickerDesc value={this.text} onChange={this.setText} /> <StickerDesc value={this.text} onChange={this.setText} />
{this.generateStickerSVG(sticker)} {this.generateStickerSVG(set, sticker)}
<div <div
className="sticker-delete" className="sticker-delete"
onMouseDown={this.onDelete} onMouseDown={this.onDelete}
@ -163,7 +164,8 @@ export class Sticker {
this.stickerArrow.style.transform = `rotate(${angle + Math.PI}rad)`; this.stickerArrow.style.transform = `rotate(${angle + Math.PI}rad)`;
}; };
generateStickerSVG = ({ set, sticker }) => ( generateStickerSVG = (set, sticker) => {
return (
<div <div
className="sticker-image" className="sticker-image"
style={{ style={{
@ -172,11 +174,13 @@ export class Sticker {
}} }}
/> />
); );
};
dumpData = () => ({ dumpData = () => ({
angle: this.angle, angle: this.angle,
latlng: { ...this.marker.getLatLng() }, latlng: { ...this.marker.getLatLng() },
sticker: this.sticker, sticker: this.sticker,
set: this.set,
text: this.text, text: this.text,
}); });

View file

@ -20,7 +20,7 @@ export class Stickers {
// this.createSticker({ latlng }); // this.createSticker({ latlng });
// }; // };
createSticker = ({ latlng, sticker, angle = 2.2, text = '' }) => { createSticker = ({ latlng, sticker, angle = 2.2, text = '', set }) => {
const marker = new Sticker({ const marker = new Sticker({
latlng, latlng,
angle, angle,
@ -28,6 +28,7 @@ export class Stickers {
map: this.map, map: this.map,
lockMapClicks: this.lockMapClicks, lockMapClicks: this.lockMapClicks,
sticker, sticker,
set,
triggerOnChange: this.triggerOnChange, triggerOnChange: this.triggerOnChange,
text, text,
}); });

View file

@ -20,9 +20,9 @@ import { DEFAULT_USER } from '$constants/auth';
import { TIPS } from '$constants/tips'; import { TIPS } from '$constants/tips';
import { import {
composeImages, composeImages,
composePoly, downloadCanvas, composePoly, composeStickers, downloadCanvas,
fetchImages, fetchImages,
getPolyPlacement, getPolyPlacement, getStickersPlacement,
getTilePlacement, getTilePlacement,
imageFetcher imageFetcher
} from '$utils/renderer'; } from '$utils/renderer';
@ -259,12 +259,15 @@ function* getRenderData() {
const geometry = getTilePlacement(); const geometry = getTilePlacement();
const points = getPolyPlacement(); const points = getPolyPlacement();
const stickers = getStickersPlacement();
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
const images = yield fetchImages(ctx, geometry); const images = yield fetchImages(ctx, geometry);
yield composeImages({ geometry, images, ctx }); yield composeImages({ geometry, images, ctx });
yield composePoly({ points, ctx }); yield composePoly({ points, ctx });
yield composeStickers({ stickers, ctx });
return yield canvas.toDataURL('image/jpeg'); return yield canvas.toDataURL('image/jpeg');
} }

View file

@ -162,7 +162,7 @@
.sticker-desc { .sticker-desc {
min-width: 60px; min-width: 60px;
z-index: -1;
height: auto; height: auto;
background: #222222; background: #222222;
position: absolute; position: absolute;

View file

@ -65,6 +65,17 @@ export const getPolyPlacement = () => (
: editor.poly.poly.getLatLngs().map((latlng) => ({ ...editor.map.map.latLngToContainerPoint(latlng) })) : editor.poly.poly.getLatLngs().map((latlng) => ({ ...editor.map.map.latLngToContainerPoint(latlng) }))
); );
export const getStickersPlacement = () => (
(!editor.stickers || editor.stickers.dumpData().length <= 0)
? []
: editor.stickers.dumpData().map(({ latlng: { lat, lng }, text, angle, sticker }) => ({
...editor.map.map.latLngToContainerPoint({ lat, lng }),
text,
sticker,
angle,
}))
);
const getImageSource = coords => replaceProviderUrl(editor.provider, coords); const getImageSource = coords => replaceProviderUrl(editor.provider, coords);
export const imageFetcher = source => new Promise((resolve, reject) => { export const imageFetcher = source => new Promise((resolve, reject) => {
@ -142,7 +153,74 @@ export const composePoly = ({ points, ctx }) => {
ctx.stroke(); ctx.stroke();
ctx.closePath(); ctx.closePath();
return true; return;
};
const measureText = (ctx, text, lineHeight) => (
text.split('\n').reduce((obj, line) => (
{
width: Math.max(ctx.measureText(line).width, obj.width),
height: obj.height + lineHeight,
}
), { width: 0, height: 0 })
);
const composeStickerArrow = (ctx, x, y, angle) => {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle + (Math.PI * 0.75));
ctx.translate(-x, -y);
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + 38, y + 20);
ctx.lineTo(x + 38, y + 38);
ctx.lineTo(x + 20, y + 38);
ctx.fill();
ctx.closePath();
ctx.restore();
};
const composeStickerText = (ctx, x, y, angle, text) => {
const rad = 56;
const tX = ((Math.cos(angle + Math.PI) * rad) - 30) + x + 28;
const tY = ((Math.sin(angle + Math.PI) * rad) - 30) + y + 29;
ctx.font = '12px "Helvetica Neue", Arial';
const lines = text.split('\n');
const { width, height } = measureText(ctx, text, 16);
// rectangle
ctx.fillStyle = '#222222';
ctx.beginPath();
ctx.rect(
tX,
tY + 3 - (height / 2) - 10,
width + 36 + 10,
height + 20,
);
ctx.closePath();
ctx.fill();
ctx.fillStyle = 'white';
lines.map((line, i) => {
ctx.fillText(
line,
tX + 36,
tY + (16 * i) + 16 - (height / 2)
);
});
};
export const composeStickers = ({ stickers, ctx }) => {
if (!stickers || stickers.length < 0) return;
stickers.map(({ x, y, angle, text }) => {
composeStickerArrow(ctx, x, y, angle);
composeStickerText(ctx, x, y, angle, text);
});
return;
}; };
export const downloadCanvas = (canvas, title) => canvas.toBlob(blob => saveAs(blob, title)); export const downloadCanvas = (canvas, title) => canvas.toBlob(blob => saveAs(blob, title));