I am using this below script to draw polygon to google map, which is working ok. I want to add searchbox to map. I tried a lot, But cannot embed searchbox.
http://bl.ocks.org/knownasilya/89a32e572989f0aff1f8
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="UTF-8">
<title>Drawing Tools</title>
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false&libraries=drawing"></script>
<style type="text/css">
#map, html, body {
padding: 0;
margin: 0;
width: 960px;
height: 700px;
}
#panel {
width: 200px;
font-family: Arial, sans-serif;
font-size: 13px;
float: right;
margin: 10px;
}
#color-palette {
clear: both;
}
.color-button {
width: 14px;
height: 14px;
font-size: 0;
margin: 2px;
float: left;
cursor: pointer;
}
#delete-button {
margin-top: 5px;
}
</style>
<script type="text/javascript">
var drawingManager;
var selectedShape;
var colors = ['#1E90FF', '#FF1493', '#32CD32', '#FF8C00', '#4B0082'];
var selectedColor;
var colorButtons = {};
function clearSelection () {
if (selectedShape) {
if (selectedShape.type !== 'marker') {
selectedShape.setEditable(false);
}
selectedShape = null;
}
}
function setSelection (shape) {
if (shape.type !== 'marker') {
clearSelection();
shape.setEditable(true);
selectColor(shape.get('fillColor') || shape.get('strokeColor'));
}
selectedShape = shape;
}
function deleteSelectedShape () {
if (selectedShape) {
selectedShape.setMap(null);
}
}
function selectColor (color) {
selectedColor = color;
for (var i = 0; i < colors.length; ++i) {
var currColor = colors[i];
colorButtons[currColor].style.border = currColor == color ? '2px solid #789' : '2px solid #fff';
}
// Retrieves the current options from the drawing manager and replaces the
// stroke or fill color as appropriate.
var polylineOptions = drawingManager.get('polylineOptions');
polylineOptions.strokeColor = color;
drawingManager.set('polylineOptions', polylineOptions);
var rectangleOptions = drawingManager.get('rectangleOptions');
rectangleOptions.fillColor = color;
drawingManager.set('rectangleOptions', rectangleOptions);
var circleOptions = drawingManager.get('circleOptions');
circleOptions.fillColor = color;
drawingManager.set('circleOptions', circleOptions);
var polygonOptions = drawingManager.get('polygonOptions');
polygonOptions.fillColor = color;
drawingManager.set('polygonOptions', polygonOptions);
}
function setSelectedShapeColor (color) {
if (selectedShape) {
if (selectedShape.type == google.maps.drawing.OverlayType.POLYLINE) {
selectedShape.set('strokeColor', color);
} else {
selectedShape.set('fillColor', color);
}
}
}
function makeColorButton (color) {
var button = document.createElement('span');
button.className = 'color-button';
button.style.backgroundColor = color;
google.maps.event.addDomListener(button, 'click', function () {
selectColor(color);
setSelectedShapeColor(color);
});
return button;
}
function buildColorPalette () {
var colorPalette = document.getElementById('color-palette');
for (var i = 0; i < colors.length; ++i) {
var currColor = colors[i];
var colorButton = makeColorButton(currColor);
colorPalette.appendChild(colorButton);
colorButtons[currColor] = colorButton;
}
selectColor(colors[0]);
}
function initialize () {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 16,
center: new google.maps.LatLng(52.25097, 20.97114),
mapTypeId: google.maps.MapTypeId.SATELLITE,
disableDefaultUI: true,
zoomControl: true
});
var polyOptions = {
strokeWeight: 0,
fillOpacity: 0.45,
editable: true,
draggable: true
};
// Creates a drawing manager attached to the map that allows the user to draw
// markers, lines, and shapes.
drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYGON,
markerOptions: {
draggable: true
},
polylineOptions: {
editable: true,
draggable: true
},
rectangleOptions: polyOptions,
circleOptions: polyOptions,
polygonOptions: polyOptions,
map: map
});
google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) {
var newShape = e.overlay;
newShape.type = e.type;
if (e.type !== google.maps.drawing.OverlayType.MARKER) {
// Switch back to non-drawing mode after drawing a shape.
drawingManager.setDrawingMode(null);
// Add an event listener that selects the newly-drawn shape when the user
// mouses down on it.
google.maps.event.addListener(newShape, 'click', function (e) {
if (e.vertex !== undefined) {
if (newShape.type === google.maps.drawing.OverlayType.POLYGON) {
var path = newShape.getPaths().getAt(e.path);
path.removeAt(e.vertex);
if (path.length < 3) {
newShape.setMap(null);
}
}
if (newShape.type === google.maps.drawing.OverlayType.POLYLINE) {
var path = newShape.getPath();
path.removeAt(e.vertex);
if (path.length < 2) {
newShape.setMap(null);
}
}
}
setSelection(newShape);
});
setSelection(newShape);
}
else {
google.maps.event.addListener(newShape, 'click', function (e) {
setSelection(newShape);
});
setSelection(newShape);
}
});
// Clear the current selection when the drawing mode is changed, or when the
// map is clicked.
google.maps.event.addListener(drawingManager, 'drawingmode_changed', clearSelection);
google.maps.event.addListener(map, 'click', clearSelection);
google.maps.event.addDomListener(document.getElementById('delete-button'), 'click', deleteSelectedShape);
buildColorPalette();
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="panel">
<div id="color-palette"></div>
<div>
<button id="delete-button">Delete Selected Shape</button>
</div>
</div>
<div id="map"></div>
</body>
</html>
I want to add searchbox with this script, After trying a lot I posted it. thank you for your precious time.
From the SearchBox example in the documentation
add the following code to your initialize function (be sure to include the places library in the API include):
// Create the search box and link it to the UI element.
var input = document.getElementById('pac-input');
var searchBox = new google.maps.places.SearchBox(input);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
// Bias the SearchBox results towards current map's viewport.
map.addListener('bounds_changed', function() {
searchBox.setBounds(map.getBounds());
});
var markers = [];
// Listen for the event fired when the user selects a prediction and retrieve
// more details for that place.
searchBox.addListener('places_changed', function() {
var places = searchBox.getPlaces();
if (places.length == 0) {
return;
}
// Clear out the old markers.
markers.forEach(function(marker) {
marker.setMap(null);
});
markers = [];
// For each place, get the icon, name and location.
var bounds = new google.maps.LatLngBounds();
places.forEach(function(place) {
if (!place.geometry) {
console.log("Returned place contains no geometry");
return;
}
var icon = {
url: place.icon,
size: new google.maps.Size(71, 71),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(17, 34),
scaledSize: new google.maps.Size(25, 25)
};
// Create a marker for each place.
markers.push(new google.maps.Marker({
map: map,
icon: icon,
title: place.name,
position: place.geometry.location
}));
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
} else {
bounds.extend(place.geometry.location);
}
});
map.fitBounds(bounds);
});
proof of concept fiddle
code snippet:
var drawingManager;
var selectedShape;
var colors = ['#1E90FF', '#FF1493', '#32CD32', '#FF8C00', '#4B0082'];
var selectedColor;
var colorButtons = {};
function clearSelection() {
if (selectedShape) {
if (selectedShape.type !== 'marker') {
selectedShape.setEditable(false);
}
selectedShape = null;
}
}
function setSelection(shape) {
if (shape.type !== 'marker') {
clearSelection();
shape.setEditable(true);
selectColor(shape.get('fillColor') || shape.get('strokeColor'));
}
selectedShape = shape;
}
function deleteSelectedShape() {
if (selectedShape) {
selectedShape.setMap(null);
}
}
function selectColor(color) {
selectedColor = color;
for (var i = 0; i < colors.length; ++i) {
var currColor = colors[i];
colorButtons[currColor].style.border = currColor == color ? '2px solid #789' : '2px solid #fff';
}
// Retrieves the current options from the drawing manager and replaces the
// stroke or fill color as appropriate.
var polylineOptions = drawingManager.get('polylineOptions');
polylineOptions.strokeColor = color;
drawingManager.set('polylineOptions', polylineOptions);
var rectangleOptions = drawingManager.get('rectangleOptions');
rectangleOptions.fillColor = color;
drawingManager.set('rectangleOptions', rectangleOptions);
var circleOptions = drawingManager.get('circleOptions');
circleOptions.fillColor = color;
drawingManager.set('circleOptions', circleOptions);
var polygonOptions = drawingManager.get('polygonOptions');
polygonOptions.fillColor = color;
drawingManager.set('polygonOptions', polygonOptions);
}
function setSelectedShapeColor(color) {
if (selectedShape) {
if (selectedShape.type == google.maps.drawing.OverlayType.POLYLINE) {
selectedShape.set('strokeColor', color);
} else {
selectedShape.set('fillColor', color);
}
}
}
function makeColorButton(color) {
var button = document.createElement('span');
button.className = 'color-button';
button.style.backgroundColor = color;
google.maps.event.addDomListener(button, 'click', function() {
selectColor(color);
setSelectedShapeColor(color);
});
return button;
}
function buildColorPalette() {
var colorPalette = document.getElementById('color-palette');
for (var i = 0; i < colors.length; ++i) {
var currColor = colors[i];
var colorButton = makeColorButton(currColor);
colorPalette.appendChild(colorButton);
colorButtons[currColor] = colorButton;
}
selectColor(colors[0]);
}
function initialize() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 16,
center: new google.maps.LatLng(52.25097, 20.97114),
mapTypeId: google.maps.MapTypeId.SATELLITE,
disableDefaultUI: true,
zoomControl: true
});
var polyOptions = {
strokeWeight: 0,
fillOpacity: 0.45,
editable: true,
draggable: true
};
// Creates a drawing manager attached to the map that allows the user to draw
// markers, lines, and shapes.
drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYGON,
markerOptions: {
draggable: true
},
polylineOptions: {
editable: true,
draggable: true
},
rectangleOptions: polyOptions,
circleOptions: polyOptions,
polygonOptions: polyOptions,
map: map
});
google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
var newShape = e.overlay;
newShape.type = e.type;
if (e.type !== google.maps.drawing.OverlayType.MARKER) {
// Switch back to non-drawing mode after drawing a shape.
drawingManager.setDrawingMode(null);
// Add an event listener that selects the newly-drawn shape when the user
// mouses down on it.
google.maps.event.addListener(newShape, 'click', function(e) {
if (e.vertex !== undefined) {
if (newShape.type === google.maps.drawing.OverlayType.POLYGON) {
var path = newShape.getPaths().getAt(e.path);
path.removeAt(e.vertex);
if (path.length < 3) {
newShape.setMap(null);
}
}
if (newShape.type === google.maps.drawing.OverlayType.POLYLINE) {
var path = newShape.getPath();
path.removeAt(e.vertex);
if (path.length < 2) {
newShape.setMap(null);
}
}
}
setSelection(newShape);
});
setSelection(newShape);
} else {
google.maps.event.addListener(newShape, 'click', function(e) {
setSelection(newShape);
});
setSelection(newShape);
}
});
// Clear the current selection when the drawing mode is changed, or when the
// map is clicked.
google.maps.event.addListener(drawingManager, 'drawingmode_changed', clearSelection);
google.maps.event.addListener(map, 'click', clearSelection);
google.maps.event.addDomListener(document.getElementById('delete-button'), 'click', deleteSelectedShape);
buildColorPalette();
// SearchBox code
// Create the search box and link it to the UI element.
var input = document.getElementById('pac-input');
var searchBox = new google.maps.places.SearchBox(input);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
// Bias the SearchBox results towards current map's viewport.
map.addListener('bounds_changed', function() {
searchBox.setBounds(map.getBounds());
});
var markers = [];
// Listen for the event fired when the user selects a prediction and retrieve
// more details for that place.
searchBox.addListener('places_changed', function() {
var places = searchBox.getPlaces();
if (places.length == 0) {
return;
}
// Clear out the old markers.
markers.forEach(function(marker) {
marker.setMap(null);
});
markers = [];
// For each place, get the icon, name and location.
var bounds = new google.maps.LatLngBounds();
places.forEach(function(place) {
if (!place.geometry) {
console.log("Returned place contains no geometry");
return;
}
var icon = {
url: place.icon,
size: new google.maps.Size(71, 71),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(17, 34),
scaledSize: new google.maps.Size(25, 25)
};
// Create a marker for each place.
markers.push(new google.maps.Marker({
map: map,
icon: icon,
title: place.name,
position: place.geometry.location
}));
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
} else {
bounds.extend(place.geometry.location);
}
});
map.fitBounds(bounds);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
#map,
html,
body {
padding: 0;
margin: 0;
width: 960px;
height: 700px;
}
#panel {
width: 200px;
font-family: Arial, sans-serif;
font-size: 13px;
float: right;
margin: 10px;
}
#color-palette {
clear: both;
}
.color-button {
width: 14px;
height: 14px;
font-size: 0;
margin: 2px;
float: left;
cursor: pointer;
}
#delete-button {
margin-top: 5px;
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry,places,drawing&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<input id="pac-input" class="controls" type="text" placeholder="Search Box">
<div id="panel">
<div id="color-palette"></div>
<div>
<button id="delete-button">Delete Selected Shape</button>
</div>
</div>
<div id="map"></div>
Related
I'm on Nuxt 2.15.4 and using Quagga2. I want to create a scanner like mobile scanners where the camera is full screen and there is a blue rectangle that the barcode must be placed inside of, and after scanning there will be a red line through the barcode canvas. With Quagga.js I can manipulate the CSS and area property in canvas but it cause 2 problems:
The actual scanning zone will be messed up and I don't know where the scanning exactly happens.
The successful red line-through style will be messed up.
Also, after a successful scan I need the red line-through to remain and not disappear and no scanning happen for a period of time (so I can decide to continue scanning or do something with result).
Here is the code I implemented on in app (also need help on activating torch and zoom):
<template>
<section id="container" class="container">
<div id="interactive" class="viewport"></div>
{{code}}
</section>
</template>
<script>
import Quagga from '#ericblade/quagga2';
export default {
data() {
return {
code:[]
};
},
methods:{
async checkDevice() {
let md = navigator.mediaDevices;
if (!md || !md.enumerateDevices) return false;
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.some((device) => "videoinput" === device.kind);
},
initQuagga(){
Quagga.init({
inputStream : {
name : "Live",
type : "LiveStream",
area: {
top: "0%",
right: "0%",
left: "0%",
bottom: "0%",
},
},
locate: false,
decoder : {
readers : [
"ean_reader",
],
}
},(err)=>{
if(err){
return
}
// this.checkCapabilities();
this.startQuagga()
});
},
// checkCapabilities(){
// var track = Quagga.CameraAccess.getActiveTrack();
// var capabilities = {};
// if (typeof track.getCapabilities === 'function') {
// capabilities = track.getCapabilities();
// }
// this.applySettingsVisibility('zoom', capabilities.zoom);
// this.applySettingsVisibility('torch', capabilities.torch);
// },
// applySetting: function(setting, value) {
// var track = Quagga.CameraAccess.getActiveTrack();
// if (track && typeof track.getCapabilities === 'function') {
// switch (setting) {
// case 'zoom':
// return track.applyConstraints({advanced: [{zoom: parseFloat(value)}]});
// case 'torch':
// return track.applyConstraints({advanced: [{torch: !!value}]});
// }
// }
// },
startQuagga(){
Quagga.start()
Quagga.onProcessed(function (result) {
let drawingCtx = Quagga.canvas.ctx.overlay,
drawingCanvas = Quagga.canvas.dom.overlay;
if (result) {
if (result.boxes) {
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
result.boxes.filter(function (box) {
return box !== result.box;
}).forEach(function (box) {
Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "#00F", lineWidth: 2});
});
}
if (result.box) {
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: "#008", lineWidth: 2});
}
if (result.codeResult && result.codeResult.code) {
Quagga.ImageDebug.drawPath(result.line, {x: 'x', y: 'y'}, drawingCtx, {color: 'red', lineWidth: 3});
}
}
})
Quagga.onDetected(this.onDetected);
},
onDetected(data) {
let barCodeData = data.codeResult.code;
console.log(barCodeData);
},
},
async mounted(){
let data = await this.checkDevice();
if (data) {
this.initQuagga();
}
},
beforeDestroy(){
Quagga.stop()
}
}
</script>
<style lang="scss">
#container {
width: 640px;
margin: 20px auto;
padding: 10px;
}
#interactive.viewport {
width: 640px;
height: 480px;
}
#interactive.viewport canvas, video {
float: left;
width: 640px;
height: 480px;
}
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer {
margin-left: -640px;
}
</style>
I am trying to convert from pell rich text editor to vue component. I have downloaded pell.js and converted it to vue component but I meet some issues now.
I transfer all datas and methods from pell to vue component.
And I called this.init function in created() method. And it shows that this.defaultActions which defined in datas() is not defined in init functions.
Please give me any advice. Thanks..
Here is my vue component
<template>
<div class="content">
<h1>pell</h1>
<div id="editor" class="pell"></div>
<div style="margin-top:20px;">
<h3>Text output:</h3>
<div id="text-output"></div>
</div>
<div style="margin-top:20px;">
<h3>HTML output:</h3>
<pre id="html-output"></pre>
</div>
</div>
</template>
<script>
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
export default {
data: ()=> ({
defaultParagraphSeparatorString : 'defaultParagraphSeparator',
formatBlock : 'formatBlock',
defaultActions: {
bold: {
icon: '<b>B</b>',
title: 'Bold',
state: function state() {
return this.queryCommandState('bold');
},
result: function result() {
return this.exec('bold');
}
},
italic: {
icon: '<i>I</i>',
title: 'Italic',
state: function state() {
return this.queryCommandState('italic');
},
result: function result() {
return this.exec('italic');
}
},
underline: {
icon: '<u>U</u>',
title: 'Underline',
state: function state() {
return this.queryCommandState('underline');
},
result: function result() {
return this.exec('underline');
}
},
strikethrough: {
icon: '<strike>S</strike>',
title: 'Strike-through',
state: function state() {
return this.queryCommandState('strikeThrough');
},
result: function result() {
return this.exec('strikeThrough');
}
},
heading1: {
icon: '<b>H<sub>1</sub></b>',
title: 'Heading 1',
result: function result() {
return this.exec('formatBlock', '<h1>');
}
},
heading2: {
icon: '<b>H<sub>2</sub></b>',
title: 'Heading 2',
result: function result() {
return this.exec('formatBlock', '<h2>');
}
},
paragraph: {
icon: '¶',
title: 'Paragraph',
result: function result() {
return this.exec('formatBlock', '<p>');
}
},
quote: {
icon: '“ ”',
title: 'Quote',
result: function result() {
return this.exec('formatBlock', '<blockquote>');
}
},
olist: {
icon: '#',
title: 'Ordered List',
result: function result() {
return this.exec('insertOrderedList');
}
},
ulist: {
icon: '•',
title: 'Unordered List',
result: function result() {
return this.exec('insertUnorderedList');
}
},
code: {
icon: '</>',
title: 'Code',
result: function result() {
return this.exec('formatBlock', '<pre>');
}
},
line: {
icon: '―',
title: 'Horizontal Line',
result: function result() {
return this.exec('insertHorizontalRule');
}
},
link: {
icon: '🔗',
title: 'Link',
result: function result() {
var url = window.prompt('Enter the link URL');
if (url) this.exec('createLink', url);
}
},
image: {
icon: '📷',
title: 'Image',
result: function result() {
var url = window.prompt('Enter the image URL');
if (url) this.exec('insertImage', url);
}
}
},
defaultClasses: {
actionbar: 'pell-actionbar',
button: 'pell-button',
content: 'pell-content',
selected: 'pell-button-selected'
},
}),
created(){
console.log("this.defaultActions", this.defaultActions);
this.init(
{
element: document.getElementById('editor'),
defaultParagraphSeparator: 'p',
// actions: [
// 'bold',
// 'italic',
// 'underline',
// 'strikethrough'
// ],
onChange: function (html) {
document.getElementById('text-output').innerHTML = html
document.getElementById('html-output').textContent = html
}
}
);
},
methods:{
addEventListener(parent, type, listener) {
return parent.addEventListener(type, listener);
},
appendChild(parent, child) {
return parent.appendChild(child);
},
createElement(tag) {
return document.createElement(tag);
},
queryCommandState(command) {
return document.queryCommandState(command);
},
queryCommandValue(command) {
return document.queryCommandValue(command);
},
exec(command) {
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
return document.execCommand(command, false, value);
},
init(settings){
Object.keys(this.defaultActions).map(function (action) {
console.log("action", action)
});
var actions = settings.actions ? settings.actions.map(function (action) {
if (typeof action === 'string') return this.defaultActions[action];
else if (this.defaultActions[action.name]) return _extends({}, this.defaultActions[action.name], action);
return action;
}) : Object.keys(this.defaultActions).map(function (action) {
console.log("action", action)
console.log("sss", this.defaultActions)
// return this.defaultActions[action];
});
var classes = _extends({}, this.defaultClasses, settings.classes);
var defaultParagraphSeparator = settings[this.defaultParagraphSeparatorString] || 'div';
var actionbar = this.createElement('div');
actionbar.className = classes.actionbar;
this.appendChild(settings.element, actionbar);
var content = settings.element.content = this.createElement('div');
content.contentEditable = true;
content.className = classes.content;
content.oninput = function (_ref) {
var firstChild = _ref.target.firstChild;
if (firstChild && firstChild.nodeType === 3) this.exec(this.formatBlock, '<' + defaultParagraphSeparator + '>');else if (content.innerHTML === '<br>') content.innerHTML = '';
settings.onChange(content.innerHTML);
};
content.onkeydown = function (event) {
if (event.key === 'Enter' && this.queryCommandValue(this.formatBlock) === 'blockquote') {
setTimeout(function () {
return this.exec(this.formatBlock, '<' + defaultParagraphSeparator + '>');
}, 0);
}
};
this.appendChild(settings.element, content);
actions.forEach(function (action) {
var button = this.createElement('button');
button.className = classes.button;
button.innerHTML = action.icon;
button.title = action.title;
button.setAttribute('type', 'button');
button.onclick = function () {
return action.result() && content.focus();
};
if (action.state) {
var handler = function handler() {
return button.classList[action.state() ? 'add' : 'remove'](classes.selected);
};
this.addEventListener(content, 'keyup', handler);
this.addEventListener(content, 'mouseup', handler);
this.addEventListener(button, 'click', handler);
}
this.appendChild(actionbar, button);
});
if (settings.styleWithCSS) this.exec('styleWithCSS');
this.exec(this.defaultParagraphSeparatorString, defaultParagraphSeparator);
return settings.element;
}
}
}
</script>
<style>
.content {
box-sizing: border-box;
margin: 0 auto;
max-width: 600px;
padding: 20px;
}
#html-output {
white-space: pre-wrap;
}
.pell {
border: 1px solid rgba(10, 10, 10, 0.1);
box-sizing: border-box; }
.pell-content {
box-sizing: border-box;
height: 300px;
outline: 0;
overflow-y: auto;
padding: 10px; }
.pell-actionbar {
background-color: #FFF;
border-bottom: 1px solid rgba(10, 10, 10, 0.1); }
.pell-button {
background-color: transparent;
border: none;
cursor: pointer;
height: 30px;
outline: 0;
width: 30px;
vertical-align: bottom; }
.pell-button-selected {
background-color: #F0F0F0; }
</style>
You should use arrow functions in the block mapping actions, preserves this from the surrounding scope
var actions = settings.actions
? settings.actions.map(action => { // arrow function here
if (typeof action === "string") return this.defaultActions[action];
else if (this.defaultActions[action.name])
return _extends({}, this.defaultActions[action.name], action);
return action
})
: Object.keys(this.defaultActions).map(action => { // arrow function here
console.log("action", action)
console.log("sss", this.defaultActions);
// return this.defaultActions[action];
});
Just for reference, you can also use the sample Vue code given in the pell repository
(or parts thereof, e.g styles)
<template>
<div>
<h6>Editor:</h6>
<div id="pell" class="pell" />
<h6>HTML Output:</h6>
<pre id="pell-html-output"></pre>
</div>
</template>
<script>
import pell from 'pell'
export default {
methods: {
ensureHTTP: str => /^https?:\/\//.test(str) && str || `http://${str}`
},
mounted () {
pell.init({
element: document.getElementById('pell'),
onChange: html => {
window.document.getElementById('pell-html-output').textContent = html
},
actions: [
'bold', 'italic', 'underline', 'strikethrough', 'heading1', 'heading2',
'paragraph', 'quote', 'olist', 'ulist', 'code', 'line',
{
name: 'image',
result: () => {
const url = window.prompt('Enter the image URL')
if (url) pell.exec('insertImage', this.ensureHTTP(url))
}
},
{
name: 'link',
result: () => {
const url = window.prompt('Enter the link URL')
if (url) pell.exec('createLink', this.ensureHTTP(url))
}
}
]
})
}
}
</script>
<style>
.pell {
border: 2px solid #000;
border-radius: 0;
box-shadow: none;
}
#pell-html-output {
margin: 0;
white-space: pre-wrap;
}
</style>
total newbie with Vue.js here, so sorry if I'm missing something obvious.
I want to load components based on the content of a variable - so if the content of my variable changes, I want the component to change accordingly. I have all components imported (e.g. import Project1 from "#/components/Project1";), which again is saved in components: {'p1': Project1}
I use <component :is="this.goTo.page"/>
with goTo.page being defined in data as data() {return {goTo: {page: "p1"}}}.
The problem seems to be the reactivness of goTo.page. I update it via this.$set(this.goTo, "page", "p2");. When running, i get the error Uncaught TypeError: Cannot read property 'page' of undefined, which I honestly don't understand, since its initialized with "p1".
import * as Matter from "matter-js";
import Project1 from "#/components/Project1";
import Project2 from "#/components/Project2";
import Project3 from "#/components/Project3";
export default {
name: "Work",
components: {
'p1': Project1,
'p2': Project2,
'p3': Project3
},
data() {
return {
goTo: {
page: "p1"
}
}
},
methods: {
startMatter: function () {
let harrie = require('#/assets/img/harrie.png');
let kub = require('#/assets/img/kub.png');
let jacs = require('#/assets/img/jacs.png');
let container = document.getElementById('threecontainerWork');
let nameTag = document.getElementById("title");
let width = container.offsetWidth;
let height = container.offsetHeight;
let engine = Matter.Engine.create({});
let renderer = Matter.Render.create({
element: document.getElementById('matterContainer'),
canvas: container,
engine: engine,
options: {
width: width,
height: height,
wireframes: false
}
})
let borderLeft = Matter.Bodies.rectangle(0, 0, 1, height * 2, {isStatic: true});
let borderRight = Matter.Bodies.rectangle(width, 0, 1, height * 2, {isStatic: true});
let roof = Matter.Bodies.rectangle(container.getBoundingClientRect().left, 0, width * 2, 1, {isStatic: true});
let ground = Matter.Bodies.rectangle(container.getBoundingClientRect().left, height, width * 2, 1, {isStatic: true});
let collider = Matter.Bodies.rectangle(container.offsetWidth / 2, container.offsetHeight, nameTag.offsetWidth, 1, {
isStatic: true,
isSensor: true,
render: {
fillStyle: 'red'
}
});
let nameTagBox = Matter.Bodies.rectangle(container.offsetWidth / 2, container.offsetHeight / 2, nameTag.offsetWidth, nameTag.offsetHeight, {isStatic: true});
let project1 = Matter.Bodies.rectangle(container.offsetWidth / 2, 100, 200, 200, {
render: {
sprite: {
texture: harrie
}
}
});
let project2 = Matter.Bodies.rectangle(201, 0, 200, 200, {
render: {
sprite: {
texture: kub
}
}
});
let project3 = Matter.Bodies.rectangle(1400, 0, 200, 200, {
render: {
sprite: {
texture: jacs
}
}
});
let polyStack = Matter.Composites.stack(container.offsetWidth / 2, 50, 2, 10, 0, 0, function (x, y) {
let sides = Math.round(Matter.Common.random(2, 8));
return Matter.Bodies.polygon(x, y, sides, Matter.Common.random(100, 100));
})
let mouse = Matter.Mouse.create(renderer.canvas);
let mouseConstraint = Matter.MouseConstraint.create(engine, {
mouse: mouse
})
let colStart = Matter.Events.on(engine, 'collisionStart', function (event) {
var pairs = event.pairs;
for (var i = 0, j = pairs.length; i != j; ++i) {
var pair = pairs[i];
if (pair.bodyA === collider) {
switch (pair.bodyB.id) {
case project1.id:
this.$set(this.goTo, 'page', "p1");
break;
case project2.id:
this.$set(this.goTo, 'page', "p2");
break;
case project3.id:
this.$set(this.goTo, 'page', "p3");
break;
}
} else if (pair.bodyB === collider) {
switch (pair.bodyA.id) {
case project1.id:
this.$set(this.goTo, 'page', "p1");
break;
case project2.id:
this.$set(this.goTo, 'page', "p2");
break;
case project3.id:
this.$set(this.goTo, 'page', "p3");
break;
}
}
}
});
let colEnd = Matter.Events.on(engine, 'collisionEnd', function (event) {
var pairs = event.pairs;
for (var i = 0, j = pairs.length; i != j; ++i) {
var pair = pairs[i];
if (pair.bodyA === collider) {
switch (pair.bodyB.id) {
case project1.id:
this.$set(this.goTo, 'page', "noview");
break;
case project2.id:
this.$set(this.goTo, 'page', "noview");
break;
case project3.id:
this.$set(this.goTo, 'page', "noview");
break;
}
} else if (pair.bodyB === collider) {
switch (pair.bodyA.id) {
case project1.id:
this.$set(this.goTo, 'page', "noview");
break;
case project2.id:
this.$set(this.goTo, 'page', "noview");
break;
case project3.id:
this.$set(this.goTo, 'page', "noview");
break;
}
}
}
});
// TODO implement Vue.set(this.goTo, 'page', "p2"); correctly -> Matter Hell
Matter.World.add(engine.world, [ground, borderLeft, borderRight, roof, collider, project1, project2, project3, nameTagBox, polyStack, mouseConstraint, colStart, colEnd]);
Matter.Engine.run(engine);
Matter.Render.run(renderer);
}
},
},
mounted() {
this.$nextTick(function () {
this.startMatter();
})
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
#upperDiv {
position: relative;
width: 98vw;
height: 81vh;
margin: auto auto 1vh auto;
display: flex;
justify-content: center;
align-items: center;
top: 9vh /*Für alle anderen auch*/
}
#title {
position: absolute;
font-family: 'Playfair Display', serif;
font-weight: 600;
font-size: 5rem;
color: #f0f0f0;
padding: 2vh 3vw;
border: 1px solid #f0f0f0;
background-color: #1d1c1c;
z-index: 2;
text-align: center;
}
.threeContainer {
width: 96vw;
height: 70vh;
z-index: 1;
margin-bottom: 0;
}
<template>
<div>
<div id="upperDiv">
<div class="threeContainer" id="matterContainer">
<canvas id="threecontainerWork" class="threeContainer"></canvas>
</div>
<p id="title">My work</p>
</div>
<component :is="goTo.page"/>
</div>
</template>
<component :is="goTo.page"/>
this is not available in <template>. Everything "inside" this, is available.
In addition to the above and per your update,
methods: {
startMatter: function () {
//some matter.js
//if body collision detected:
this.$set(this.goTo, 'page', "p2");
},
},
The second argument of this.$set should be a string with the key name you wish to update.
If that doesn't work, your nextTick might be the problem. As in, there is no nextTick after mounted, unless something is updated.
mounted() {
this.$nextTick(function () {
this.startMatter();
})
}
Updated based on comments.
I believe that it's this line (line 108?), which overrides your this.
let colStart = Matter.Events.on(engine, 'collisionStart', function (event) {
Try instead:
let colStart = Matter.Events.on(engine, 'collisionStart', (event) => {
Function has it own scope /this. Vue normally binds to it, however I think that doesn't work as you're in matters callback now. The arrow function ensures the scope is lexical.
Same goes for:
let colEnd = Matter.Events.on(engine, 'collisionEnd', function (event) {,
change it to:
let colEnd = Matter.Events.on(engine, 'collisionEnd', (event) => {
This is the behavior I want to achieve in Vue.js Here is the Js fiddle example i am trying to make: https://jsfiddle.net/richardcwc/ukqhf54k/
//Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//Variables
var canvasx = $(canvas).offset().left;
var canvasy = $(canvas).offset().top;
var last_mousex = last_mousey = 0;
var mousex = mousey = 0;
var mousedown = false;
//Mousedown
$(canvas).on('mousedown', function(e) {
last_mousex = parseInt(e.clientX-canvasx);
last_mousey = parseInt(e.clientY-canvasy);
mousedown = true;
});
//Mouseup
$(canvas).on('mouseup', function(e) {
mousedown = false;
});
//Mousemove
$(canvas).on('mousemove', function(e) {
mousex = parseInt(e.clientX-canvasx);
mousey = parseInt(e.clientY-canvasy);
if(mousedown) {
ctx.clearRect(0,0,canvas.width,canvas.height); //clear canvas
ctx.beginPath();
var width = mousex-last_mousex;
var height = mousey-last_mousey;
ctx.rect(last_mousex,last_mousey,width,height);
ctx.strokeStyle = 'black';
ctx.lineWidth = 10;
ctx.stroke();
}
//Output
$('#output').html('current: '+mousex+', '+mousey+'<br/>last: '+last_mousex+', '+last_mousey+'<br/>mousedown: '+mousedown);
});
I am using a library called Konva.js. Right now I am able to free drawing in Vue.js with Konva.js. But When I try to draw the rectangle with mousemove. It does not work correctly. I am not sure what causes the issue. Thanks for any help! Here is my work on
Code sandbox
This is the behavior I found out for my work. It only draws the rectangle after the mouse move event and then mouse click event.
<template>
<v-stage
ref="stage"
:config="stageSize"
#mousemove="handleMouseMove"
#mouseDown="handleMouseDown"
#mouseUp="handleMouseUp"
>
<v-layer ref="layer">
<v-text
ref="text"
:config="{
x: 10,
y: 10,
fontSize: 20,
text: text,
fill: 'black',
}"
/>
<v-rect
v-for="(rec, index) in recs"
:key="index"
:config="{
x: Math.min(rec.startPointX, rec.startPointX + rec.width),
y: Math.min(rec.startPointY, rec.startPointY + rec.height),
width: Math.abs(rec.width),
height: Math.abs(rec.height),
fill: 'rgb(0,0,0,0)',
stroke: 'black',
strokeWidth: 3,
}"
/>
</v-layer>
</v-stage>
</template>
<script>
const width = window.innerWidth;
const height = window.innerHeight;
export default {
data() {
return {
stageSize: {
width: width,
height: height,
},
text: "Try to draw a rectangle",
lines: [],
isDrawing: false,
recs: [],
};
},
methods: {
handleMouseDown(event) {
this.isDrawing = true;
const pos = this.$refs.stage.getNode().getPointerPosition();
this.setRecs([
...this.recs,
{ startPointX: pos.x, startPointY: pos.y, width: 0, height: 0 },
]);
},
handleMouseUp() {
this.isDrawing = false;
},
setRecs(element) {
this.recs = element;
},
handleMouseMove(event) {
// no drawing - skipping
if (!this.isDrawing) {
return;
}
// console.log(event);
const point = this.$refs.stage.getNode().getPointerPosition();
// handle rectangle part
let curRec = this.recs[this.recs.length - 1];
curRec.width = point.x - curRec.startPointX;
curRec.height = point.y - curRec.startPointY;
},
},
};
</script>
Demo: https://codesandbox.io/s/vue-konva-drawings-rectangles-ivjtu?file=/src/App.vue
Hey everyone so I have a Konvajs application that works great as a video editor running on the vuejs library. However, I want to capture the canvas and create a seamless video at 60 fps. In order to do this, I am trying to utilize the CCapturejs library. It kind of works except for now the playback of the webm is really fast and still a bit choppy. Can any of ya'll look at this code and help me find the problem? Thanks.
<template>
<div>
<button #click="render">Render</button>
<button #click="stop">stop</button>
<h2>Backgrounds</h2>
<template v-for="background in backgrounds">
<img
:src="background.poster"
class="backgrounds"
#click="changeBackground(background.video)"
/>
</template>
<h2>Images</h2>
<template v-for="image in images">
<img
:src="image.source"
#click="addImage(image)"
class="images"
/>
</template>
<br />
<button #click="addText">Add Text</button>
<button v-if="selectedNode" #click="removeNode">
Remove selected {{ selectedNode.type }}
</button>
<label>Font:</label>
<select v-model="selectedFont">
<option value="Arial">Arial</option>
<option value="Courier New">Courier New</option>
<option value="Times New Roman">Times New Roman</option>
<option value="Desoto">Desoto</option>
<option value="Kalam">Kalam</option>
</select>
<label>Font Size</label>
<input type="number" v-model="selectedFontSize" />
<label>Font Style:</label>
<select v-model="selectedFontStyle">
<option value="normal">Normal</option>
<option value="bold">Bold</option>
<option value="italic">Italic</option>
</select>
<label>Color:</label>
<input type="color" v-model="selectedColor" />
<button
v-if="selectedNode && selectedNode.type === 'text'"
#click="updateText"
>
Update Text
</button>
<template v-if="selectedNode && selectedNode.lottie">
<input type="text" v-model="text">
<button #click="updateAnim(selectedNode.image)">
Update Animation
</button>
</template>
<br />
<video
id="preview"
v-show="preview"
:src="preview"
:width="width"
:height="height"
preload="auto"
controls
/>
<a v-if="file" :href="file" download="dopeness.mp4">download</a>
<div id="container"></div>
</div>
</template>
<script>
import lottie from "lottie-web";
import CCapture from "../ccapture";
import * as anim from "../AEAnim/anim.json";
import * as anim2 from "../AEAnim/anim2.json";
import * as anim3 from "../AEAnim/anim3.json";
import * as anim4 from "../AEAnim/anim4.json";
import * as anim5 from "../AEAnim/anim5.json";
export default {
data() {
return {
source: null,
stage: null,
layer: null,
video: null,
animations: [],
text: "",
animationData: null,
captures: [],
capturer: null,
backgrounds: [
{
poster: "/api/files/stock/3oref310k1uud86w/poster/poster.jpg",
video:
"/api/files/stock/3oref310k1uud86w/main/1080/3oref310k1uud86w_1080.mp4"
},
{
poster: "https://i2.wp.com/livinglifefearless.co/wp-content/uploads/2019/08/Forrest-Gump-25-1.jpg?fit=1920%2C1220&ssl=1",
video: "/api/files/jedi/run_forest_run.mp4"
},
{
poster: "/api/files/stock/3yj2e30tk5x6x0ww/poster/poster.jpg",
video:
"/api/files/stock/3yj2e30tk5x6x0ww/main/1080/3yj2e30tk5x6x0ww_1080.mp4"
},
{
poster: "/api/files/stock/2ez931ik1mggd6j/poster/poster.jpg",
video:
"/api/files/stock/2ez931ik1mggd6j/main/1080/2ez931ik1mggd6j_1080.mp4"
},
{
poster: "/api/files/stock/yxrt4ej4jvimyk15/poster/poster.jpg",
video:
"/api/files/stock/yxrt4ej4jvimyk15/main/1080/yxrt4ej4jvimyk15_1080.mp4"
},
{
poster:
"https://images.costco-static.com/ImageDelivery/imageService?profileId=12026540&itemId=100424771-847&recipeName=680",
video: "/api/files/jedi/surfing.mp4"
},
{
poster:
"https://thedefensepost.com/wp-content/uploads/2018/04/us-soldiers-afghanistan-4308413-1170x610.jpg",
video: "/api/files/jedi/soldiers.mp4"
}
],
images: [
{ source: "/api/files/jedi/solo.jpg" },
{ source: "api/files/jedi/yoda.jpg" },
{ source: "api/files/jedi/yodaChristmas.jpg" },
{ source: "api/files/jedi/darthMaul.jpg" },
{ source: "api/files/jedi/darthMaul1.jpg" },
{ source: "api/files/jedi/trump.jpg" },
{ source: "api/files/jedi/hat.png" },
{ source: "api/files/jedi/trump.png" },
{ source: "api/files/jedi/bernie.png" },
{ source: "api/files/jedi/skywalker.png" },
{ source: "api/files/jedi/vader.gif" },
{ source: "api/files/jedi/vader2.gif" },
{ source: "api/files/jedi/yoda.gif" },
{ source: "api/files/jedi/kylo.gif" },
{
source: "https://media3.giphy.com/media/R3IxJW14a3QNa/source.gif",
animation: anim
},
{
source: "https://bestanimations.com/Text/Cool/cool-story-3.gif",
animation: anim2
},
{
source: "https://freefrontend.com/assets/img/css-text-animations/HTML-CSS-Animated-Text-Fill.gif",
animation: anim3
},
{
source: "api/files/jedi/slider.gif",
animation: anim4
},
{
source: "api/files/jedi/zoomer.gif",
animation: anim5
}
],
backgroundVideo: null,
imageGroups: [],
anim: null,
selectedNode: null,
selectedFont: "Arial",
selectedColor: "black",
selectedFontSize: 20,
selectedFontStyle: "normal",
width: 1920,
height: 1080,
texts: [],
preview: null,
file: null,
canvas: null
};
},
mounted: function() {
this.initCanvas();
},
methods: {
changeBackground(source) {
this.source = source;
this.video.src = this.source;
this.anim.stop();
this.anim.start();
this.video.play();
},
removeNode() {
if (this.selectedNode && this.selectedNode.type === "text") {
this.selectedNode.transformer.destroy(
this.selectedNode.text.transformer
);
this.selectedNode.text.destroy(this.selectedNode.text);
this.texts.splice(this.selectedNode.text.index - 1, 1);
this.selectedNode = null;
this.layer.draw();
} else if (this.selectedNode && this.selectedNode.type == "image") {
this.selectedNode.group.destroy(this.selectedNode);
this.imageGroups.splice(this.selectedNode.group.index - 1, 1);
if (this.selectedNode.lottie) {
cancelAnimationFrame(this.animations.animFrame);
this.selectedNode.lottie.destroy();
this.animations.splice(this.selectedNode.lottie.index - 1, 1);
}
this.selectedNode = null;
this.layer.draw();
}
},
async addImage(imageToAdd, isUpdate) {
let lottieAnimation = null;
let imageObj = null;
const type = imageToAdd.source.slice(imageToAdd.source.lastIndexOf("."));
const vm = this;
function process(img) {
return new Promise((resolve, reject) => {
img.onload = () => resolve({ width: img.width, height: img.height });
});
}
imageObj = new Image();
imageObj.src = imageToAdd.source;
imageObj.width = 200;
imageObj.height = 200;
await process(imageObj);
if (type === ".gif" && !imageToAdd.animation) {
const canvas = document.createElement("canvas");
canvas.setAttribute("id", "gif");
async function onDrawFrame(ctx, frame) {
ctx.drawImage(frame.buffer, frame.x, frame.y);
// redraw the layer
vm.layer.draw();
}
gifler(imageToAdd.source).frames(canvas, onDrawFrame);
canvas.onload = async () => {
canvas.parentNode.removeChild(canvas);
};
imageObj = canvas;
const gif = new Image();
gif.src = imageToAdd.source;
const gifImage = await process(gif);
imageObj.width = gifImage.width;
imageObj.height = gifImage.height;
} else if (imageToAdd.animation) {
if(!isUpdate){this.text = "new text";}
const canvas = document.createElement("canvas");
canvas.style.width = 1920;
canvas.style.height= 1080;
canvas.setAttribute("id", "animationCanvas");
const ctx = canvas.getContext("2d");
const div = document.createElement("div");
div.setAttribute("id", "animationContainer");
div.style.display = "none";
canvas.style.display = "none";
this.animationData = imageToAdd.animation.default;
for(let i =0; i <this.animationData.layers.length; i++){
for(let b =0; b<this.animationData.layers[i].t.d.k.length; b++){
this.animationData.layers[i].t.d.k[b].s.t = this.text;
}
}
lottieAnimation = lottie.loadAnimation({
container: div, // the dom element that will contain the animation
renderer: "svg",
loop: true,
autoplay: true,
animationData: this.animationData
});
lottieAnimation.imgSrc = imageToAdd.source;
lottieAnimation.text = this.text;
const svg = await div.getElementsByTagName("svg")[0];
async function updateSvg() {
const xml = new XMLSerializer().serializeToString(svg);
const svg64 = window.btoa(xml);
const b64Start = "data:image/svg+xml;base64,";
const image64 = b64Start + svg64;
imageObj = new Image({ width: canvas.width, height: canvas.height });
imageObj.src = image64;
await process(imageObj);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(imageObj, 0, 0, canvas.width, canvas.height);
vm.layer.draw();
updateSvg();
};
const animFrame = requestAnimationFrame(updateSvg);
this.animations.push({ lottie: lottieAnimation, animFrame });
imageObj = canvas;
canvas.onload = async () => {
canvas.parentNode.removeChild(canvas);
};
}
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
width: imageObj.width,
height: imageObj.height,
position: (0, 0),
strokeWidth: 10,
stroke: "blue",
strokeEnabled: false
});
const group = new Konva.Group({
draggable: true
});
// add the shape to the layer
addAnchor(group, 0, 0, "topLeft");
addAnchor(group, imageObj.width, 0, "topRight");
addAnchor(group, imageObj.width, imageObj.height, "bottomRight");
addAnchor(group, 0, imageObj.height, "bottomLeft");
imageObj = null;
image.on("click", function () {
vm.hideAllHelpers();
vm.selectedNode = {
type: "image",
group,
lottie: lottieAnimation,
image: imageToAdd
};
if(lottieAnimation && lottieAnimation.text){vm.text = lottieAnimation.text}
group.find("Circle").show();
vm.layer.draw();
});
image.on("mouseover", function(evt) {
if (vm.selectedNode && vm.selectedNode.type === "image") {
const index = image.getParent().index;
const groupId = vm.selectedNode.group.index;
if (index != groupId) {
evt.target.strokeEnabled(true);
vm.layer.draw();
}
} else {
evt.target.strokeEnabled(true);
vm.layer.draw();
}
});
image.on("mouseout", function(evt) {
evt.target.strokeEnabled(false);
vm.layer.draw();
});
vm.hideAllHelpers();
group.find("Circle").show();
group.add(image);
vm.layer.add(group);
vm.imageGroups.push(group);
vm.selectedNode = {
type: "image",
group,
lottie: lottieAnimation,
image: imageToAdd
};
function update(activeAnchor) {
const group = activeAnchor.getParent();
let topLeft = group.get(".topLeft")[0];
let topRight = group.get(".topRight")[0];
let bottomRight = group.get(".bottomRight")[0];
let bottomLeft = group.get(".bottomLeft")[0];
let image = group.get("Image")[0];
let anchorX = activeAnchor.getX();
let anchorY = activeAnchor.getY();
// update anchor positions
switch (activeAnchor.getName()) {
case "topLeft":
topRight.y(anchorY);
bottomLeft.x(anchorX);
break;
case "topRight":
topLeft.y(anchorY);
bottomRight.x(anchorX);
break;
case "bottomRight":
bottomLeft.y(anchorY);
topRight.x(anchorX);
break;
case "bottomLeft":
bottomRight.y(anchorY);
topLeft.x(anchorX);
break;
}
image.position(topLeft.position());
let width = topRight.getX() - topLeft.getX();
let height = bottomLeft.getY() - topLeft.getY();
if (width && height) {
image.width(width);
image.height(height);
}
}
function addAnchor(group, x, y, name) {
let stage = vm.stage;
let layer = vm.layer;
let anchor = new Konva.Circle({
x: x,
y: y,
stroke: "#666",
fill: "#ddd",
strokeWidth: 2,
radius: 8,
name: name,
draggable: true,
dragOnTop: false
});
anchor.on("dragmove", function() {
update(this);
layer.draw();
});
anchor.on("mousedown touchstart", function() {
group.draggable(false);
this.moveToTop();
});
anchor.on("dragend", function() {
group.draggable(true);
layer.draw();
});
// add hover styling
anchor.on("mouseover", function() {
let layer = this.getLayer();
document.body.style.cursor = "pointer";
this.strokeWidth(4);
layer.draw();
});
anchor.on("mouseout", function() {
let layer = this.getLayer();
document.body.style.cursor = "default";
this.strokeWidth(2);
layer.draw();
});
group.add(anchor);
}
},
async updateAnim(image){
this.addImage(image, true);
this.removeNode();
},
hideAllHelpers() {
for (let i = 0; i < this.texts.length; i++) {
this.texts[i].transformer.hide();
}
for (let b = 0; b < this.imageGroups.length; b++) {
this.imageGroups[b].find("Circle").hide();
}
},
render(){
this.video.currentTime =0;
this.video.loop = false;
this.captureCanvas();
},
captureCanvas (){
this.capturer = new CCapture( {
verbose: true,
//display: false,
framerate: 60,
//motionBlurFrames: 0,
//quality: 100,
format: "webm",
timeLimit: 0
//frameLimit: 0,
//autoSaveTime: 0,
} );
this.capturer.start();
this.updateCapturer();
},
stop(){
this.capturer.stop();
this.capturer.save();
},
updateCapturer(){
console.log("holy crap I was called");
console.log("this is the canvas", this.canvas);
let rAF;
if(this.capturer){
this.capturer.capture(this.canvas)
rAF = requestAnimationFrame(this.updateCapturer);
} else {
cancelAnimationFrame(rAf);
}
},
updateText() {
if (this.selectedNode && this.selectedNode.type === "text") {
const text = this.selectedNode.text;
const transformer = this.selectedNode.transformer;
text.fontSize(this.selectedFontSize);
text.fontFamily(this.selectedFont);
text.fontStyle(this.selectedFontStyle);
text.fill(this.selectedColor);
this.layer.draw();
}
},
addText() {
const vm = this;
const text = new Konva.Text({
text: "new text " + (vm.texts.length + 1),
x: 50,
y: 80,
fontSize: this.selectedFontSize,
fontFamily: this.selectedFont,
fontStyle: this.selectedFontStyle,
fill: this.selectedColor,
align: "center",
width: this.width * 0.5,
draggable: true
});
const transformer = new Konva.Transformer({
node: text,
keepRatio: true,
enabledAnchors: ["top-left", "top-right", "bottom-left", "bottom-right"]
});
text.on("click", async () => {
for (let i = 0; i < this.texts.length; i++) {
let item = this.texts[i];
if (item.index === text.index) {
let transformer = item.transformer;
this.selectedNode = { type: "text", text, transformer };
this.selectedFontSize = text.fontSize();
this.selectedFont = text.fontFamily();
this.selectedFontStyle = text.fontStyle();
this.selectedColor = text.fill();
vm.hideAllHelpers();
transformer.show();
transformer.moveToTop();
text.moveToTop();
vm.layer.draw();
break;
}
}
});
text.on("mouseover", () => {
transformer.show();
this.layer.draw();
});
text.on("mouseout", () => {
if (
(this.selectedNode &&
this.selectedNode.text &&
this.selectedNode.text.index != text.index) ||
(this.selectedNode && this.selectedNode.type === "image") ||
!this.selectedNode
) {
transformer.hide();
this.layer.draw();
}
});
text.on("dblclick", () => {
text.hide();
transformer.hide();
vm.layer.draw();
let textPosition = text.absolutePosition();
let stageBox = vm.stage.container().getBoundingClientRect();
let areaPosition = {
x: stageBox.left + textPosition.x,
y: stageBox.top + textPosition.y
};
let textarea = document.createElement("textarea");
window.document.body.appendChild(textarea);
textarea.value = text.text();
textarea.style.position = "absolute";
textarea.style.top = areaPosition.y + "px";
textarea.style.left = areaPosition.x + "px";
textarea.style.width = text.width() - text.padding() * 2 + "px";
textarea.style.height = text.height() - text.padding() * 2 + 5 + "px";
textarea.style.fontSize = text.fontSize() + "px";
textarea.style.border = "none";
textarea.style.padding = "0px";
textarea.style.margin = "0px";
textarea.style.overflow = "hidden";
textarea.style.background = "none";
textarea.style.outline = "none";
textarea.style.resize = "none";
textarea.style.lineHeight = text.lineHeight();
textarea.style.fontFamily = text.fontFamily();
textarea.style.transformOrigin = "left top";
textarea.style.textAlign = text.align();
textarea.style.color = text.fill();
let rotation = text.rotation();
let transform = "";
if (rotation) {
transform += "rotateZ(" + rotation + "deg)";
}
let px = 0;
let isFirefox =
navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
if (isFirefox) {
px += 2 + Math.round(text.fontSize() / 20);
}
transform += "translateY(-" + px + "px)";
textarea.style.transform = transform;
textarea.style.height = "auto";
textarea.focus();
// start
function removeTextarea() {
textarea.parentNode.removeChild(textarea);
window.removeEventListener("click", handleOutsideClick);
text.show();
transformer.show();
transformer.forceUpdate();
vm.layer.draw();
}
function setTextareaWidth(newWidth) {
if (!newWidth) {
// set width for placeholder
newWidth = text.placeholder.length * text.fontSize();
}
// some extra fixes on different browsers
let isSafari = /^((?!chrome|android).)*safari/i.test(
navigator.userAgent
);
let isFirefox =
navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
if (isSafari || isFirefox) {
newWidth = Math.ceil(newWidth);
}
let isEdge =
document.documentMode || /Edge/.test(navigator.userAgent);
if (isEdge) {
newWidth += 1;
}
textarea.style.width = newWidth + "px";
}
textarea.addEventListener("keydown", function(e) {
// hide on enter
// but don't hide on shift + enter
if (e.keyCode === 13 && !e.shiftKey) {
text.text(textarea.value);
removeTextarea();
}
// on esc do not set value back to node
if (e.keyCode === 27) {
removeTextarea();
}
});
textarea.addEventListener("keydown", function(e) {
let scale = text.getAbsoluteScale().x;
setTextareaWidth(text.width() * scale);
textarea.style.height = "auto";
textarea.style.height =
textarea.scrollHeight + text.fontSize() + "px";
});
function handleOutsideClick(e) {
if (e.target !== textarea) {
text.text(textarea.value);
removeTextarea();
}
}
setTimeout(() => {
window.addEventListener("click", handleOutsideClick);
});
// end
});
text.transformer = transformer;
this.texts.push(text);
this.layer.add(text);
this.layer.add(transformer);
this.hideAllHelpers();
this.selectedNode = { type: "text", text, transformer };
transformer.show();
this.layer.draw();
},
initCanvas() {
const vm = this;
this.stage = new Konva.Stage({
container: "container",
width: vm.width,
height: vm.height
});
this.layer = new Konva.Layer();
this.stage.add(this.layer);
let video = document.createElement("video");
video.setAttribute("id", "video");
video.setAttribute("ref", "video");
if (this.source) {
video.src = this.source;
}
video.preload = "auto";
video.loop = "loop";
video.style.display = "none";
this.video = video;
this.backgroundVideo = new Konva.Image({
image: vm.video,
draggable: false
});
this.video.addEventListener("loadedmetadata", function(e) {
vm.backgroundVideo.width(vm.width);
vm.backgroundVideo.height(vm.height);
});
this.video.addEventListener("ended", () => {
console.log("the video ended");
this.stop();
this.capturer = null;
this.anim.stop();
this.anim.start();
this.video.loop = "loop";
this.video.play();
});
this.anim = new Konva.Animation(function() {
console.log("animation called");
// do nothing, animation just need to update the layer
}, vm.layer);
this.layer.add(this.backgroundVideo);
this.layer.draw();
const canvas = document.getElementsByTagName("canvas")[0];
canvas.style.border = "3px solid red";
this.canvas = canvas;
}
}
};
</script>
<style scoped>
body {
margin: 0;
padding: 0;
background-color: #f0f0f0;
}
.backgrounds,
.images {
width: 100px;
height: 100px;
padding-left: 2px;
padding-right: 2px;
}
</style>
Use this code to pause the video and take screenshots. You can than use Whammy to generate a webm and convert it to whatever file format you like with ffmpeg
generateVideo(){
const vid = new Whammy.fromImageArray(this.captures, 30);
vid.name = "project_id_238.webm";
vid.lastModifiedDate = new Date();
this.file = URL.createObjectURL(vid);
},
async pauseAll(){
this.pauseVideo();
if(this.animations.length){
this.pauseLotties()
}
this.captures.push(this.canvas.toDataURL('image/webp'));
if(!this.ended){
setTimeout(()=>{
this.pauseAll();
}, 500);
}
},
async pauseVideo(){
console.log("curretTime",this.video.currentTime);
console.log("duration", this.video.duration);
this.video.pause();
const oneFrame = 1/30;
this.video.currentTime += oneFrame;
},
async pauseLotties(){
lottie.freeze();
for(let i =0; i<this.animations.length; i++){
let step =0;
let animation = this.animations[i].lottie;
if(animation.currentFrame<=animation.totalFrames){
step = animation.currentFrame + animation.totalFrames/30;
}
lottie.goToAndStop(step, true, animation.name);
}
},