Can’t access to custom canvas attribute in Vue - vue.js

Has anyone used this component with Vue?
https://www.npmjs.com/package/aframe-draw-component.
I want to use Advanced Usage with “aframe-draw-component”.
it works with raw html but not vue.js. codepen example
// html
<a-scene fog="type: exponential; color:#000">
<a-sky acanvas rotation="-5 -10 0"></a-sky>
</a-scene>
// js
const chars = 'ABCDEFGHIJKLMNOPQRWXYZ'.split('')
const font_size = 8
AFRAME.registerComponent("acanvas", {
dependencies: ["draw"],
init: function(){
console.log(this.el.components)
this.draw = this.el.components.draw // get access to the draw component
this.draw.canvas.width = '512'
this.draw.canvas.height = '512'
this.cnvs = this.draw.canvas
const columns = this.cnvs.width / font_size
this.drops = []
for (let x = 0; x < columns; x++) {
this.drops[x] = 1
}
this.ctx = this.draw.ctx
},
tick: function() {
this.ctx.fillStyle = 'rgba(0,0,0,0.05)'
this.ctx.fillRect(0, 0, this.cnvs.width, this.cnvs.height)
this.ctx.fillStyle = '#0F0'
this.ctx.font = font_size + 'px helvetica'
for(let i = 0; i < this.drops.length; i++) {
const txt = chars[Math.floor(Math.random() * chars.length)]
this.ctx.fillText(txt, i * font_size, this.drops[i] * font_size)
if(this.drops[i] * font_size > this.cnvs.height && Math.random() > 0.975) {
this.drops[i] = 0 // back to the top!
}
this.drops[i] = this.drops[i] + 1
}
this.draw.render()
}
})
No matter where I put in Vue component, I get this error:
App.vue?b405:124 Uncaught (in promise) TypeError: Cannot read property ‘canvas’ of undefined
at NewComponent.init (eval at
It can’t find the custom dependency “draw”.
Can somebody help me ?
Thanks.

The canvas element is at Your disposal in the el.components.draw.canvas reference.
You can either create Your standalone vue script, or attach it to an existing component like this:
AFRAME.registerComponent("vue-draw", {
init: function() {
const vm = new Vue({
el: 'a-plane',
mounted: function() {
setTimeout(function() {
var draw = document.querySelector('a-plane').components.draw; /
var ctx = draw.ctx;
var canvas = draw.canvas;
ctx.fillStyle = 'red';
ctx.fillRect(68, 68, 120, 120);
draw.render();
}, 100)
}
});
}
})
Working fiddle: https://jsfiddle.net/6bado2q2/2/
Basically, I just accessed the canvas, and told it to draw a rectangle.
Please keep in mind, that Your error may persist when You try to access the canvas before the draw component managed to create it.
My no-brainer solution was just a timeout. Since the scene loads, and starts rendering faster, than the canvas is being made, I would suggest making a interval checking if the canvas element is defined somehow, and then fire the vue.js script.Also keep in mind, that You can work on existing canvas elements with the build in canvas texture: https://aframe.io/docs/0.6.0/components/material.html

Related

Remove image overlay layer in Leaflet

I'm trying to place a sketch on a building on the map with Leaflet. I succeeded in this as well, I can place and scale an image in svg format on the map. But when I add a new sketch to the map, I want the old sketch to be deleted from the map.
map.removeLayer(sketchLayer)
sketchLayer.removeFrom(map)
map.removeLayer(L.sketchLayer)
i tried these
My codes,
splitCoordinate (value) {
const topLeftCoordinates = value.top_left.split(',')
const topRightCoordinates = value.top_right.split(',')
const bottomLeftCoordinates = value.bottom_left.split(',')
this.initMap(topLeftCoordinates, topRightCoordinates, bottomLeftCoordinates)
},
initMap (topLeftCoordinates, topRightCoordinates, bottomLeftCoordinates) {
let map = this.$refs.map.mapObject
map.flyTo(new L.LatLng(topLeftCoordinates[0], topLeftCoordinates[1]), 20, { animation: true })
const vm = this
var topleft = L.latLng(topLeftCoordinates[0], topLeftCoordinates[1])
var topright = L.latLng(topRightCoordinates[0], topRightCoordinates[1])
var bottomleft = L.latLng(bottomLeftCoordinates[0], bottomLeftCoordinates[1])
var sketchLayer = L.imageOverlay.rotated(vm.imgUrl, topleft, topright, bottomleft, {
opacity: 1,
interactive: true
})
map.addLayer(sketchLayer)
}
Fix:
The error was related to the javascript scope. I defined the sketchLayer in the data and my problem solved.

How does Puppeteer handle the click Object / DevTools Protocol Chromium/Chrome

I need to know how puppeteer handles the click object, as well as Chromium DevTools API. I've tried to research it on my own and have found myself not being able to find the actual code that handles it.
The reason why I need to know is I'm developing a wrapper that tests events in code for testing Web Pages, and was looking to see if implementing a event handling routine is beneficial instead of using puppeteers interface of events (clicks and taps an hover, as well as other events that might be needed like touch events, or scrolling)
Here is how far I've gotten:
Puppeteer API uses the Frame Logic of DevTools to contact API:
https://github.com/puppeteer/puppeteer/blob/master/lib/Page.js
/**
* #param {string} selector
* #param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options
*/
click(selector, options = {}) {
return this.mainFrame().click(selector, options);
}
/**
* #return {!Puppeteer.Frame}
*/
/**
* #param {!Protocol.Page.Frame} framePayload`
*/
_onFrameNavigated(framePayload) {
const isMainFrame = !framePayload.parentId;
let frame = isMainFrame ? this._mainFrame : this._frames.get(framePayload.id);
assert(isMainFrame || frame, 'We either navigate top level or have old version of the navigated frame');
// Detach all child frames first.
if (frame) {
for (const child of frame.childFrames())
this._removeFramesRecursively(child);
}
if (isMainFrame) {
if (frame) {
// Update frame id to retain frame identity on cross-process navigation.
this._frames.delete(frame._id);
frame._id = framePayload.id;
} else {
// Initial main frame navigation.
frame = new Frame(this, this._client, null, framePayload.id);
}
this._frames.set(framePayload.id, frame);
this._mainFrame = frame;
}
This is as far as I have gotten because I've tried to look up the Page Protocol but I can't figure out what happens there.
Any help would be appreciated, even in research.
The main parts are happening in JSHandle here:
async click(options) {
await this._scrollIntoViewIfNeeded();
const {x, y} = await this._clickablePoint();
await this._page.mouse.click(x, y, options);
}
It scrolls until the element is in viewport (otherwise it won't click) which is happening here, then it finds the clickable coordinates on the element using DevTools API here:
async _clickablePoint() {
const [result, layoutMetrics] = await Promise.all([
this._client.send('DOM.getContentQuads', {
objectId: this._remoteObject.objectId
}).catch(debugError),
this._client.send('Page.getLayoutMetrics'),
]);
if (!result || !result.quads.length)
throw new Error('Node is either not visible or not an HTMLElement');
// Filter out quads that have too small area to click into.
const {clientWidth, clientHeight} = layoutMetrics.layoutViewport;
const quads = result.quads.map(quad => this._fromProtocolQuad(quad)).map(quad => this._intersectQuadWithViewport(quad, clientWidth, clientHeight)).filter(quad => computeQuadArea(quad) > 1);
if (!quads.length)
throw new Error('Node is either not visible or not an HTMLElement');
// Return the middle point of the first quad.
const quad = quads[0];
let x = 0;
let y = 0;
for (const point of quad) {
x += point.x;
y += point.y;
}
return {
x: x / 4,
y: y / 4
};
}
and finally it moves the mouse to the coordinate here and clicks on it here
async click(x, y, options = {}) {
const {delay = null} = options;
if (delay !== null) {
await Promise.all([
this.move(x, y),
this.down(options),
]);
await new Promise(f => setTimeout(f, delay));
await this.up(options);
} else {
await Promise.all([
this.move(x, y),
this.down(options),
this.up(options),
]);
}
}
which uses DevTools API to interact with mouse here
async down(options = {}) {
const {button = 'left', clickCount = 1} = options;
this._button = button;
await this._client.send('Input.dispatchMouseEvent', {
type: 'mousePressed',
button,
x: this._x,
y: this._y,
modifiers: this._keyboard._modifiers,
clickCount
});
}

how to re initialize webglglobe with vue

I am using http://www.webglearth.org/api for a globe on my vue app.
I have a route called globe that initializes the globe with new WE.map. when I change routes and go back it re initializes but re adds scripts to my head which jam up the globe tiles from loading. Is there a way to keep and re use an instantiated object? Or any tips that could help with this
I tried saving my created globe in the store and re using it though it wont reload without doing a WE.map and that method adds the headers
this method is being called on vue create
initialize(data) {
var earth = WE.map("earth_div");
WE.tileLayer(
"https://webglearth.github.io/webglearth2-offline/{z}/{x}/{y}.jpg",
{
tileSize: 256,
bounds: [[-85, -180], [85, 180]],
minZoom: 0,
maxZoom: 16,
attribution: "WebGLEarth example",
tms: true
}
).addTo(earth);
data.forEach(doc => {
let latlng = [];
console.log(typeof doc.data().images);
console.log(doc.data().galleryTitle);
latlng[0] = doc.data().lat;
latlng[1] = doc.data().lng;
console.log(latlng);
var marker = WE.marker(latlng).addTo(earth);
marker.element.id = doc.data().safeTitle;
let popup = `${doc.data().location}`;
marker
.bindPopup(popup, { maxWidth: 150, closeButton: true })
.openPopup();
marker.element.onclick = e => {
console.log(e.target.parentElement.id);
var gallery = e.target.parentElement.id;
this.$router.push({ path: "/gallery/" + gallery });
};
});
// Start a simple rotation animation
var before = null;
requestAnimationFrame(function animate(now) {
console.log('animating')
var c = earth.getPosition();
var elapsed = before ? now - before : 0;
before = now;
earth.setCenter([c[0], c[1] + 0.1 * (elapsed / 30)]);
// requestAnimationFrame(animate);
});
earth.setView([46.8011, 8.2266], 3);
this.$store.commit('setEarth',earth)
},
I would like the globe to re initiate without re adding headers.

Area map into a bootstrap tab not working

I have a bootstrap tabs that is working well.
I have an area map that is working well if it is not inserted into a tab.
I use the Responsive Image Maps jQuery Plugin from Matt Stow, also works fine.
The symptom:
Then I put the area map into one of the tabs, a not active by default.
Then I click on the tab to make it shown. So the img is well shown.
But the area map does not working. I can't see the clickable rect.
But if I manually resize my navigator, then the area map works.
The page: https://boutique.bilp.fr/71-les-pieds-de-poteaux.html
Select tab "Guide de choix", the white rectangles should be clickable. THey are not until I manually resize the window.
The cause:
The responsible is the Responsive Image Maps jQuery Plugin. In its code, it makes a call to the jquery .width() method to obtain the width of the img where the map should works. And because the parent (tab) is hidden, the returned width is wrong. And it uses it to resize the map... with bad values. The map is then so small that it seems to not work.
Thanks for your help.
One solution is to modify the Responsive Image Maps jQuery Plugin by making ancestors visible before calling width().
Original code:
/*
* rwdImageMaps jQuery plugin v1.6
*
* Allows image maps to be used in a responsive design by recalculating the area coordinates to match the actual image size on load and window.resize
*
* Copyright (c) 2016 Matt Stow
* https://github.com/stowball/jQuery-rwdImageMaps
* http://mattstow.com
* Licensed under the MIT license
*/
;(function($) {
$.fn.rwdImageMaps = function() {
var $img = this;
var rwdImageMap = function() {
$img.each(function() {
if (typeof($(this).attr('usemap')) == 'undefined')
return;
var that = this,
$that = $(that);
// Since WebKit doesn't know the height until after the image has loaded, perform everything in an onload copy
$('<img />').on('load', function() {
var attrW = 'width',
attrH = 'height',
w = $that.attr(attrW),
h = $that.attr(attrH);
if (!w || !h) {
var temp = new Image();
temp.src = $that.attr('src');
if (!w)
w = temp.width;
if (!h)
h = temp.height;
}
var wPercent = $that.width()/100,
hPercent = $that.height()/100,
map = $that.attr('usemap').replace('#', ''),
c = 'coords';
$('map[name="' + map + '"]').find('area').each(function() {
var $this = $(this);
if (!$this.data(c))
$this.data(c, $this.attr(c));
var coords = $this.data(c).split(','),
coordsPercent = new Array(coords.length);
for (var i = 0; i < coordsPercent.length; ++i) {
if (i % 2 === 0)
coordsPercent[i] = parseInt(((coords[i]/w)*100)*wPercent);
else
coordsPercent[i] = parseInt(((coords[i]/h)*100)*hPercent);
}
$this.attr(c, coordsPercent.toString());
});
}).attr('src', $that.attr('src'));
});
};
$(window).resize(rwdImageMap).trigger('resize');
return this;
};
})(jQuery);
The modified code:
/*
* rwdImageMaps jQuery plugin v1.6
*
* Allows image maps to be used in a responsive design by recalculating the area coordinates to match the actual image size on load and window.resize
*
* Copyright (c) 2016 Matt Stow
* https://github.com/stowball/jQuery-rwdImageMaps
* http://mattstow.com
* Licensed under the MIT license
*/
;(function($) {
$.fn.rwdImageMaps = function() {
var $img = this;
var rwdImageMap = function() {
$img.each(function() {
if (typeof($(this).attr('usemap')) == 'undefined')
return;
var that = this,
$that = $(that);
// Since WebKit doesn't know the height until after the image has loaded, perform everything in an onload copy
$('<img />').on('load', function() {
// Modif BC : make ancestors visible so .width() can return the right value
//************************************************
var hidden_ancestors = [];
$that.parents().each(function() {
if ($(this).css('display') == 'none')
{
$(this).show();
hidden_ancestors.push($(this));
};
});
// END Modif BC
var attrW = 'width',
attrH = 'height',
w = $that.attr(attrW),
h = $that.attr(attrH);
if (!w || !h) {
var temp = new Image();
temp.src = $that.attr('src');
if (!w)
w = temp.width;
if (!h)
h = temp.height;
}
var wPercent = $that.width()/100,
hPercent = $that.height()/100,
map = $that.attr('usemap').replace('#', ''),
c = 'coords';
$('map[name="' + map + '"]').find('area').each(function() {
var $this = $(this);
if (!$this.data(c))
$this.data(c, $this.attr(c));
var coords = $this.data(c).split(','),
coordsPercent = new Array(coords.length);
for (var i = 0; i < coordsPercent.length; ++i) {
if (i % 2 === 0)
coordsPercent[i] = parseInt(((coords[i]/w)*100)*wPercent);
else
coordsPercent[i] = parseInt(((coords[i]/h)*100)*hPercent);
}
$this.attr(c, coordsPercent.toString());
});
// Modif BC : Restore invisibility on ancestors
//*********************************************
jQuery.each(hidden_ancestors, function(index, value)
{
$(value).css({display: ''});
});
// END Modif BC
}).attr('src', $that.attr('src'));
});
};
$(window).resize(rwdImageMap).trigger('resize');
return this;
};
})(jQuery);
I will propose this improvment to Matt Stow, the author

How to implement methods and state for vue.js component

I'm a beginner in VueJS. And as part of my learning process, I'm building a knob for my Pomodoro app. This is my fiddle.
I copied the knob code from codepen, which is implemented using jquery. As you can see in the fiddle most of the job is done by jquery.
I need to try and do this using Vue.js, using its methods and states.
How to refactor this code to a better Vue.JS code? Any suggestions much appreciated.
Vue.component('timer', {
mounted() {
var knob = $('.knob');
var angle = 0;
var minangle = 0;
var maxangle = 270;
var xDirection = "";
var yDirection = "";
var oldX = 0;
var oldY = 0;
function moveKnob(direction) {
if(direction == 'up') {
if((angle + 2) <= maxangle) {
angle = angle + 2;
setAngle();
}
}
else if(direction == 'down') {
if((angle - 2) >= minangle) {
angle = angle - 2;
setAngle();
}
}
}
function setAngle() {
// rotate knob
knob.css({
'-moz-transform':'rotate('+angle+'deg)',
'-webkit-transform':'rotate('+angle+'deg)',
'-o-transform':'rotate('+angle+'deg)',
'-ms-transform':'rotate('+angle+'deg)',
'transform':'rotate('+angle+'deg)'
});
// highlight ticks
var activeTicks = (Math.round(angle / 10) + 1);
$('.tick').removeClass('activetick');
$('.tick').slice(0,activeTicks).addClass('activetick');
// update % value in text
var pc = Math.round((angle/270)*100);
$('.current-value').text(pc+'%');
}
var RAD2DEG = 180 / Math.PI;
knob.centerX = knob.offset().left + knob.width()/2;
knob.centerY = knob.offset().top + knob.height()/2;
var offset, dragging=false;
knob.mousedown(function(e) {
dragging = true;
offset = Math.atan2(knob.centerY - e.pageY, e.pageX - knob.centerX);
})
$(document).mouseup(function() {
dragging = false
})
$(document).mousemove(function(e) {
if (dragging) {
if (oldX < e.pageX) {
xDirection = "right";
} else {
xDirection = "left";
}
oldX = e.pageX;
if(xDirection === "left") {
moveKnob('down');
} else {
moveKnob('up');
}
return false;
}
})
}
});
This example runs without jQuery.
https://jsfiddle.net/guanzo/d6vashmu/6/
Declare all the variables you need in the data function.
Declare all functions under the methods property.
Declare variables that are derived from other variables in the computed property, such as knobStyle, activeTicks, and currentValue, which are all computed from angle. Whenever angle changes, these 3 computed properties will automatically update.
Regarding the general usage of Vue, you should focus on manipulating the data, and letting Vue update the DOM for you.