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('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: '' },
// address: { type: String, required: true },
version: { type: Number, default: 2 },
route: { type: Array },
stickers: { type: Array },
route: { type: Array, default: [] },
stickers: { type: Array, default: [] },
owner: { type: Schema.Types.ObjectId, ref: 'User' },
logo: { type: String, default: 'DEFAULT' },
distance: { type: Number, default: 0 },

View file

@ -8,7 +8,7 @@ module.exports = async (req, res) => {
path: 'routes',
options: {
limit: 100,
sort: { updated: 1 },
sort: { updated_at: -1 },
}
});
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 => (
Object.keys(el).length === 4
Object.keys(el).length === 5
&& el.latlng
&& Object.keys(el.latlng).length === 2
&& el.latlng.lat

View file

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

View file

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

View file

@ -10,7 +10,7 @@ import classnames from 'classnames';
export class Sticker {
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.latlng = latlng;
@ -18,6 +18,7 @@ export class Sticker {
this.isDragging = false;
this.map = map;
this.sticker = sticker;
this.set = set;
this.editable = true;
this.triggerOnChange = triggerOnChange;
this.direction = 'right';
@ -38,7 +39,7 @@ export class Sticker {
onMouseUp={this.onDragStop}
>
<StickerDesc value={this.text} onChange={this.setText} />
{this.generateStickerSVG(sticker)}
{this.generateStickerSVG(set, sticker)}
<div
className="sticker-delete"
onMouseDown={this.onDelete}
@ -163,20 +164,23 @@ export class Sticker {
this.stickerArrow.style.transform = `rotate(${angle + Math.PI}rad)`;
};
generateStickerSVG = ({ set, sticker }) => (
<div
className="sticker-image"
style={{
generateStickerSVG = (set, sticker) => {
return (
<div
className="sticker-image"
style={{
backgroundImage: `url('${STICKERS[set].url}`,
backgroundPosition: `${-STICKERS[set].layers[sticker].off * 72} 50%`,
}}
/>
);
/>
);
};
dumpData = () => ({
angle: this.angle,
latlng: { ...this.marker.getLatLng() },
sticker: this.sticker,
set: this.set,
text: this.text,
});

View file

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

View file

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

View file

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

View file

@ -65,6 +65,17 @@ export const getPolyPlacement = () => (
: 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);
export const imageFetcher = source => new Promise((resolve, reject) => {
@ -142,7 +153,74 @@ export const composePoly = ({ points, ctx }) => {
ctx.stroke();
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));