I'm trying to use DomMakers and DomIcons inside a ShadowRoot. When the markers load, I get this error:
Uncaught TypeError: Cannot read properties of undefined (reading 'getPropertyValue')
at rm.Ga (mapsjs-core.js:350:435)
at kn (mapsjs-core.js:376:338)
at S.Ga (mapsjs-core.js:376:52)
at T.de (mapsjs-core.js:408:437)
at $o (eval at <anonymous> (mapsjs-core.js:73:36), <anonymous>:5:219)
at Zo (eval at <anonymous> (mapsjs-core.js:73:36), <anonymous>:4:425)
at fp.g (eval at <anonymous> (mapsjs-core.js:73:36), <anonymous>:16:301)
This happens because mapsjs-core can't find the canvas element, since it's inside a ShadowRoot. Here is the code snippet where the error occurs:
var r = g.style;
f.push({
Aj: r.getPropertyValue(tm),
bo: r.getPropertyPriority(tm),
style: r
});
g is supposed to be the map canvas, but inside a ShadowRoot it's the document element
I'm using Here Maps API for Javascript v3.1.30.7 on a React app.
If I change to Markers and Icons the problem is gone, but I lose the interactivity I need.
It works only in this way:
https://jsfiddle.net/ba2oL057/2/
function drawCanvas() {
var canvas = document.createElement('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.fillRect(25, 25, 100, 100);
ctx.clearRect(45, 45, 60, 60);
ctx.strokeRect(50, 50, 50, 50);
}
return canvas;
}
/**
* Create a marker that is capable of receiving DOM events and add it
* to the map.
*
* #param {H.Map} map A HERE Map instance within the application
*/
function addDomMarker(map) {
function setToShadow(outerElement){
var innerElement = document.createElement('div');
outerElement.attachShadow({mode: 'open'});
outerElement.shadowRoot.innerHTML = `
<div></div>
`;
let outerShElem = outerElement.shadowRoot.querySelectorAll('div')[0];
outerShElem.style.userSelect = 'none';
outerShElem.style.webkitUserSelect = 'none';
outerShElem.style.msUserSelect = 'none';
outerShElem.style.mozUserSelect = 'none';
outerShElem.style.cursor = 'default';
innerElement.style.color = 'red';
innerElement.style.backgroundColor = 'blue';
innerElement.style.border = '2px solid black';
innerElement.style.font = 'normal 12px arial';
innerElement.style.lineHeight = '12px'
innerElement.style.paddingTop = '2px';
innerElement.style.paddingLeft = '4px';
innerElement.style.width = '20px';
innerElement.style.height = '20px';
// add negative margin to inner element
// to move the anchor to center of the div
innerElement.style.marginTop = '-10px';
innerElement.style.marginLeft = '-10px';
outerShElem.appendChild(innerElement);
outerShElem.appendChild(drawCanvas());
// Add text to the DOM element
innerElement.innerHTML = 'C';
outerShElem.addEventListener('mouseover', changeOpacity);
outerShElem.addEventListener('mouseout', changeOpacityToOne);
}
var outerElement = document.createElement('div');
//document.body.appendChild(outerElement);
function changeOpacity(evt) {
evt.target.style.opacity = 0.6;
};
function changeOpacityToOne(evt) {
evt.target.style.opacity = 1;
};
//create dom icon and add/remove opacity listeners
var domIcon = new H.map.DomIcon(outerElement, {
// the function is called every time marker enters the viewport
onAttach: function(clonedElement, domIcon, domMarker) {
setToShadow(clonedElement);
},
// the function is called every time marker leaves the viewport
onDetach: function(clonedElement, domIcon, domMarker) {
let outerShElem = clonedElement.shadowRoot.querySelectorAll('div')[0];
outerShElem.removeEventListener('mouseover', changeOpacity);
outerShElem.removeEventListener('mouseout', changeOpacityToOne);
}
});
// Marker for Chicago Bears home
var bearsMarker = new H.map.DomMarker({lat: 41.8625, lng: -87.6166}, {
icon: domIcon
});
map.addObject(bearsMarker);
//setTimeout(()=>{map.removeObject(bearsMarker);}, 0);
}
/**
* Boilerplate map initialization code starts below:
*/
//Step 1: initialize communication with the platform
// In your own code, replace variable window.apikey with your own apikey
var platform = new H.service.Platform({
apikey: window.apikey
});
var defaultLayers = platform.createDefaultLayers();
//Step 2: initialize a map - this map is centered over Chicago.
var map = new H.Map(document.getElementById('map'),
defaultLayers.vector.normal.map,{
center: {lat:41.881944, lng:-87.627778},
zoom: 11,
pixelRatio: window.devicePixelRatio || 1
});
// add a resize listener to make sure that the map occupies the whole container
window.addEventListener('resize', () => map.getViewPort().resize());
//Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Now use the map as required...
addDomMarker(map);
Related
Hi i'm looking for drag step for each drag item, let say 200px
below image is demonstrating it:
i have searched a lot but did not find any solution related to that.
Question: The imaginary drag box step should be set to 200px, in other words it should jump to 200px
Full view codepen: https://codepen.io/eabangalore/pen/GaPzJw?editors=1000
Below snippet is not working please see codepen above
<!--
Copyright (c) 2006-2013, JGraph Ltd
Dynamic toolbar example for mxGraph. This example demonstrates changing the
state of the toolbar at runtime.
-->
<html>
<head>
<title>Toolbar example for mxGraph</title>
<!-- Sets the basepath for the library if not in same directory -->
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
function setGraphData(){
var graphState = {"tagName":"mxGraphModel","children":[{"tagName":"root","children":[{"tagName":"mxCell","attributes":{"id":"0"}},{"tagName":"mxCell","attributes":{"id":"1","parent":"0"}},{"tagName":"mxCell","attributes":{"id":"2","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"400","y":"130","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"3","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"661","y":"101","width":"100","height":"40","as":"geometry"}}]}]}]};
localStorage.setItem('graphState',JSON.stringify(graphState));
}
function html2json(html){
if(html.nodeType==3){
return {
"tagName":"#text",
"content":html.textContent
}
}
var element = {
"tagName":html.tagName
};
if(html.getAttributeNames().length>0){
element.attributes = html.getAttributeNames().reduce(
function(acc,at){acc[at]=html.getAttribute(at); return acc;},
{}
);
}
if(html.childNodes.length>0){
element.children = Array.from(html.childNodes)
.filter(
function(el){
return el.nodeType!=3
||el.textContent.trim().length>0
})
.map(function(el){return html2json(el);});
}
return element;
}
function json2html(json){
var xmlDoc = document.implementation.createDocument(null, json.tagName);
var addAttributes = function(jsonNode, node){
if(jsonNode.attributes){
Object.keys(jsonNode.attributes).map(
function(name){
node.setAttribute(name,jsonNode.attributes[name]);
}
);
}
}
var addChildren = function(jsonNode,node){
if(jsonNode.children){
jsonNode.children.map(
function(jsonChildNode){
json2htmlNode(jsonChildNode,node);
}
);
}
}
var json2htmlNode = function(jsonNode,parent){
if(jsonNode.tagName=="#text"){
return xmlDoc.createTextNode(jsonNode.content);
}
var node = xmlDoc.createElement(jsonNode.tagName);
addAttributes(jsonNode,node);
addChildren(jsonNode,node);
parent.appendChild(node);
}
addAttributes(json,xmlDoc.firstElementChild);
addChildren(json,xmlDoc.firstElementChild);
return xmlDoc;
}
</script>
<!-- Loads and initializes the library -->
<script type="text/javascript" src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<!-- Example code -->
<script type="text/javascript">
// Program starts here. Creates a sample graph in the
// DOM node with the specified ID. This function is invoked
// from the onLoad event handler of the document (see below).
function main()
{
setGraphData();
// Checks if browser is supported
if (!mxClient.isBrowserSupported())
{
// Displays an error message if the browser is
// not supported.
mxUtils.error('Browser is not supported!', 200, false);
}
else
{
// Defines an icon for creating new connections in the connection handler.
// This will automatically disable the highlighting of the source vertex.
mxConnectionHandler.prototype.connectImage = new mxImage('images/connector.gif', 16, 16);
// Creates the div for the toolbar
var tbContainer = document.createElement('div');
tbContainer.style.position = 'absolute';
tbContainer.style.overflow = 'hidden';
tbContainer.style.padding = '2px';
tbContainer.style.left = '0px';
tbContainer.style.top = '0px';
tbContainer.style.width = '24px';
tbContainer.style.bottom = '0px';
document.body.appendChild(tbContainer);
// Creates new toolbar without event processing
var toolbar = new mxToolbar(tbContainer);
toolbar.enabled = false
// Creates the div for the graph
var container = document.createElement('div');
container.style.position = 'absolute';
container.style.overflow = 'hidden';
container.style.left = '24px';
container.style.top = '0px';
container.style.right = '0px';
container.style.bottom = '0px';
container.style.background = 'url("editors/images/grid.gif")';
document.body.appendChild(container);
// Workaround for Internet Explorer ignoring certain styles
if (mxClient.IS_QUIRKS)
{
document.body.style.overflow = 'hidden';
new mxDivResizer(tbContainer);
new mxDivResizer(container);
}
// Creates the model and the graph inside the container
// using the fastest rendering available on the browser
var model = new mxGraphModel();
var graph = new mxGraph(container, model);
// Enables new connections in the graph
graph.setConnectable(true);
graph.setMultigraph(false);
// Stops editing on enter or escape keypress
var keyHandler = new mxKeyHandler(graph);
var rubberband = new mxRubberband(graph);
var addVertex = function(icon, w, h, style)
{
var vertex = new mxCell(null, new mxGeometry(0, 0, w, h), style);
vertex.setVertex(true);
var img = addToolbarItem(graph, toolbar, vertex, icon);
img.enabled = true;
graph.getSelectionModel().addListener(mxEvent.CHANGE, function()
{
var tmp = graph.isSelectionEmpty();
mxUtils.setOpacity(img, (tmp) ? 100 : 20);
img.enabled = tmp;
});
};
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rectangle.gif', 100, 40, '');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rounded.gif', 100, 40, 'shape=rounded');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/ellipse.gif', 40, 40, 'shape=ellipse');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rhombus.gif', 40, 40, 'shape=rhombus');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/triangle.gif', 40, 40, 'shape=triangle');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/cylinder.gif', 40, 40, 'shape=cylinder');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/actor.gif', 30, 40, 'shape=actor');
// read state on load
if(window.localStorage.graphState){
var doc = json2html(JSON.parse(localStorage.graphState));
var dec = new mxCodec(doc);
dec.decode(doc.documentElement, graph.getModel());
}
// save state on change
graph.getModel().addListener('change',function(){
var codec = new mxCodec();
window.localStorage.graphState = JSON.stringify(html2json(codec.encode(
graph.getModel()
)));
});
}
}
function addToolbarItem(graph, toolbar, prototype, image)
{
// Function that is executed when the image is dropped on
// the graph. The cell argument points to the cell under
// the mousepointer if there is one.
var funct = function(graph, evt, cell, x, y)
{
graph.stopEditing(false);
var vertex = graph.getModel().cloneCell(prototype);
vertex.geometry.x = x;
vertex.geometry.y = y;
graph.addCell(vertex);
graph.setSelectionCell(vertex);
}
// Creates the image which is used as the drag icon (preview)
var img = toolbar.addMode(null, image, function(evt, cell)
{
var pt = this.graph.getPointForEvent(evt);
funct(graph, evt, cell, pt.x, pt.y);
});
// Disables dragging if element is disabled. This is a workaround
// for wrong event order in IE. Following is a dummy listener that
// is invoked as the last listener in IE.
mxEvent.addListener(img, 'mousedown', function(evt)
{
// do nothing
});
// This listener is always called first before any other listener
// in all browsers.
mxEvent.addListener(img, 'mousedown', function(evt)
{
if (img.enabled == false)
{
mxEvent.consume(evt);
}
});
mxUtils.makeDraggable(img, graph, funct);
return img;
}
</script>
</head>
<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
<body onload="main();" >
</body>
</html>
Changed how value is set to the graph state:
function generateData(x1, y1, x2, y2){
var graphState = {"tagName":"mxGraphModel","children":[{"tagName":"root","children":[{"tagName":"mxCell","attributes":{"id":"0"}},{"tagName":"mxCell","attributes":{"id":"1","parent":"0"}},{"tagName":"mxCell","attributes":{"id":"2","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x": x1,"y": y1 ,"width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"3","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x": x2,"y": y2,"width":"100","height":"40","as":"geometry"}}]}]}]};
return graphState;
}
function setGraphData(){
var graphState = generateData("400", "130", "661", "101");
localStorage.setItem('graphState',JSON.stringify(graphState));
}
Refresh the data after page load:
if(window.localStorage.graphState){
var doc = json2html(JSON.parse(localStorage.graphState));
var dec = new mxCodec(doc);
dec.decode(doc.documentElement, graph.getModel());
setTimeout(function() {
var doc = json2html(generateData("400", "30", "661", "101"));
var dec = new mxCodec(doc);
dec.decode(doc.documentElement, graph.getModel());
}, 5000);
}
This will move the box up after a gap of 5 seconds.
CodePen
Everything works fine using 3.31, but when I use the 3.32 or 3.33 script my overlay fails to load. Here is my custom overlay class:
function MapOverlay(bounds, image, map) {
// Now initialize all properties.
var sw = new google.maps.LatLng(bounds.southWest.latitude, bounds.southWest.longitude);
var ne = new google.maps.LatLng(bounds.northEast.latitude, bounds.northEast.longitude);
//if (westernCorner.longitude > easternCorner.longitude)
this.bounds = new google.maps.LatLngBounds(sw, ne);
this.image = image;
this.map = map;
// We define a property to hold the image's
// div. We'll actually create this div
// upon receipt of the add() method so we'll
// leave it null for now.
this.div = null;
// Explicitly call setMap() on this overlay
this.setMap(map);
}
MapOverlay.prototype = new google.maps.OverlayView();
MapOverlay.prototype.onAdd = function() {
// Note: an overlay's receipt of onAdd() indicates that
// the map's panes are now available for attaching
// the overlay to the map via the DOM.
// Create the DIV and set some basic attributes.
var div = document.createElement('DIV');
div.style.border = "none";
div.style.borderWidth = "0px";
div.style.position = "absolute";
// Create an IMG element and attach it to the DIV.
var img = document.createElement("img");
img.src = this.image;
img.style.width = "100%";
img.style.height = "100%";
div.appendChild(img);
// Set the overlay's div_ property to this DIV
this.div = div;
// We add an overlay to a map via one of the map's panes.
// We'll add this overlay to the overlayImage pane.
var panes = this.getPanes();
panes.overlayLayer.appendChild(div);
};
MapOverlay.prototype.draw = function() {
// Size and position the overlay. We use a southwest and northeast
// position of the overlay to peg it to the correct position and size.
// We need to retrieve the projection from this overlay to do this.
var overlayProjection = this.getProjection();
// Retrieve the southwest and northeast coordinates of this overlay
// in latlngs and convert them to pixels coordinates.
// We'll use these coordinates to resize the DIV.
var sw = overlayProjection.fromLatLngToDivPixel(this.bounds.getSouthWest());
var ne = overlayProjection.fromLatLngToDivPixel(this.bounds.getNorthEast());
// Resize the image's DIV to fit the indicated dimensions.
var div = this.div;
div.style.left = sw.x + 'px';
div.style.top = ne.y + 'px';
console.log("ne.x: " + ne.x + " sw.x " + sw.x + " width: " + (ne.x - sw.x));
div.style.width = (ne.x - sw.x) + 'px';
div.style.height = (sw.y - ne.y) + 'px';
};
MapOverlay.prototype.onRemove = function() {
this.div.parentNode.removeChild(this.div);
this.div = null;
};
// Note that the visibility property must be a string enclosed in quotes
MapOverlay.prototype.hide = function() {
if (this.div) {
this.div.style.visibility = "hidden";
}
};
MapOverlay.prototype.show = function() {
if (this.div) {
this.div.style.visibility = "visible";
}
};
And here is where the overlay is set up:
addImageOverlay: function(bounds, url, hidden) {
var ne = new google.maps.LatLng(bounds.northEast.latitude,
bounds.northEast.longitude);
var sw = new google.maps.LatLng(bounds.southWest.latitude,
bounds.southWest.longitude);
var b = new google.maps.LatLngBounds(sw, ne);
if(this.imageOverlay){
this.imageOverlay.setMap(null);
this.imageOverlay = null;
}
this.imageOverlay = new MapOverlay(bounds, url, this.map);
this.imageOverlay.setMap(this.map);
if (hidden === true) {
this.imageOverlay.setMap(null);
} else {
this.imageOverlay.setMap(this.map);
}
return this.imageOverlay;
}
The error I am getting is:
Cannot read property 'parentNode' of null
at MapOverlay.onRemove (MapOverlay.js:90)
From what I can tell so far, the onAdd() method does not appear to be getting called when I call setMap(map). Therefore, this.div is null when onRemove() is called (thus the NPE). Map is populated and valid as far as I can tell. Again, this is only happening in 3.32 and 3.33 of Google Maps API. I can't find any change in the documentation that would be causing this and have been stuck for a day now. I can stick with 3.31 for now, but it is supposedly being sunset in August.
MapOverlay.prototype.onRemove = function() {
if (this.div && this.div.parentNode) {
this.div.parentNode.removeChild(this.div);
}
this.div = null;
};
Added IF block and this will solves problem.
Please find the reference link below:-
1. https://github.com/googlemaps/v3-utility-library/issues/393
2. https://github.com/enyo/dropzone/issues/1083
I try to position the Google Maps searchbox variable. A list shows all markers and under those markers there should be the searchbox. As the list grows the searchbox moves down. I use this library: Angular Google Maps
There is a directive called <ui-gmap-search-box>. But it must be inside <div id="map_canvas"> </div> and the list is outside of that div.
I think you should use the custom control functions provided by the Google Maps API.
You can position your controls on the top left corner of your maps, and create click event listeners for your control and do your work from there.
Refer to the documentation for more information on Custom Controls.
Also, I made some quick changes to this sample, and made this example here. Hope it helps.
var map;
//var chicago = new google.maps.LatLng(41.85, -87.65);
/**
* The CenterControl adds a control to the map that recenters the map on Chicago.
* This constructor takes the control DIV as an argument.
* #constructor
*/
controlUIs = [];
function CenterControl(controlDiv, map) {
locations = [new google.maps.LatLng(37.7833, -122.4167),
new google.maps.LatLng(37.3382, -121.8863),
new google.maps.LatLng(37.4223662, -122.0839445),
new google.maps.LatLng(37.8694772, -122.2577238),
new google.maps.LatLng(37.7919615, -122.2287941),
new google.maps.LatLng(37.6256441, -122.0413544)
];
for (var i = 0; i < locations.length; i++) {
// Set CSS for the control border
var controlUI = document.createElement('div');
controlUI.style.backgroundColor = '#fff';
controlUI.style.border = '2px solid #fff';
controlUI.style.borderRadius = '3px';
controlUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
controlUI.style.cursor = 'pointer';
controlUI.style.marginBottom = '22px';
controlUI.style.textAlign = 'center';
controlUI.title = 'Click to recenter the map';
controlUIs[i] = controlUI;
controlDiv.appendChild(controlUIs[i]);
// Set CSS for the control interior
var controlText = document.createElement('div');
controlText.style.color = 'rgb(25,25,25)';
controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
controlText.style.fontSize = '16px';
controlText.style.lineHeight = '38px';
controlText.style.paddingLeft = '5px';
controlText.style.paddingRight = '5px';
controlText.innerHTML = 'Location ' + i;
controlText.location = locations[i];
controlUIs[i].appendChild(controlText);
// Setup the click event listeners: simply set the map to
// Chicago
google.maps.event.addDomListener(controlUIs[i], 'click', function(click) {
map.setCenter(click.target.location)
});
}
}
function initialize() {
var mapDiv = document.getElementById('map-canvas');
var mapOptions = {
zoom: 10,
center: new google.maps.LatLng(37.6326196, -122.1568744)
}
map = new google.maps.Map(mapDiv, mapOptions);
// Create the DIV to hold the control and
// call the CenterControl() constructor passing
// in this DIV.
var centerControlDiv = document.createElement('div');
var centerControl = new CenterControl(centerControlDiv, map);
centerControlDiv.index = 1;
map.controls[google.maps.ControlPosition.TOP_LEFT].push(centerControlDiv);
}
google.maps.event.addDomListener(window, 'load', initialize);
html,
body,
#map-canvas {
height: 100%;
margin: 0;
padding: 0;
}
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true"></script>
<div id="map-canvas"></div>
I used the Google Maps API Search box instead of the library's search box.
main.js:
var searchBox = function() {
var input = document.getElementById('autocomplete');
var searchBox = new google.maps.places.SearchBox(input);
google.maps.event.addListener(searchBox, 'places_changed', function() {
});
}; // end searchBox
The function searchBox() is executed in $scope.init().
index.html:
<input id="autocomplete" ng-model="newPlace" type="text">
My index.js looks like this
'use strict';
// Famous dependencies
var DOMElement = require('famous/dom-renderables/DOMElement');
var FamousEngine = require('famous/core/FamousEngine');
var CustomNode = require('./CustomNode');
var Animation = require('./components/Animation');
// Boilerplate code to make your life easier
FamousEngine.init();
var scene = FamousEngine.createScene('body');
var rootNode = scene.addChild();
var plane = rootNode.addChild(new CustomNode('url(images/plane_100x100.png)', '#B3E5FC', 200, 200, 100, 100));
var animation = new Animation(plane);
animation.start();
where CustomNode.js is
var DOMElement = require('famous/dom-renderables/DOMElement');
var FamousEngine = require('famous/core/FamousEngine');
var Transitionable = require('famous/transitions/Transitionable');
var Size = require('famous/components/Size');
var Node = require('famous/core/Node');
function CustomNode(bgUrl, bgColor, xpos, ypos, width, height) {
Node.call(this);
this.setSizeMode('absolute', 'absolute')
.setAbsoluteSize(width, height)
.setPosition(xpos,ypos);
this.nodeDomElement = new DOMElement(this);
this.nodeDomElement.setProperty('backgroundImage', bgUrl);
this.nodeDomElement.setProperty('zIndex', '2');
this.nodeDomElement.setProperty('background-color', bgColor);
}
CustomNode.prototype = Object.create(Node.prototype);
CustomNode.prototype.constructor = CustomNode;
module.exports = CustomNode;
And Animation.js looks like this
var FamousEngine = require('famous/core/FamousEngine');
var Transitionable = require('famous/transitions/Transitionable');
// A component that will animate a node's position in x.
function Animation (node) {
// store a reference to the node
this.node = node;
// get an id from the node so that we can update
this.id = node.addComponent(this);
// create a new transitionable to drive the animation
this.xPosition = new Transitionable(100);
}
Animation.prototype.start = function start () {
// request an update to start the animation
this.node.requestUpdate(this.id);
// begin driving the animation
this.xPosition.from(100).to(1000, {duration: 1000});
this.node.requestUpdate(this.id);
};
Animation.prototype.onUpdate = function onUpdate () {
// while the transitionable is still transitioning
// keep requesting updates
if (this.xPosition.isActive()) {
// set the position of the component's node
// every frame to the value of the transitionable
this.node.setPosition(this.xPosition.get());
this.node.requestUpdateOnNextTick(this.id);
}
};
module.exports = Animation;
But when I run famous dev, I get the following error
Uncaught TypeError: this._queue[1] is not a function
Please note that I am working off of the develop branch of famous/engine
This code is posted on github
https://github.com/codesdk/famous_engine_issue_debug_animation
for easy cloning
Please use the instructions in the README
Be sure to pass the correct arguments into Transitionable#to:
this.xPosition.from(100).to(1000, 'linear', 1000);
You may have mixed up the set and to methods of Transitionable.
this.xPosition.from(100).to(1000, {duration: 1000});
Should be:
this.xPosition.from(100).to(1000, 'linear', 1000);
Animation.prototype.start = function start () {
// request an update to start the animation
this.node.requestUpdate(this.id);
// begin driving the animation
this.xPosition.from(100).to(1000, 'linear', 1000);
this.node.requestUpdate(this.id);
};
Remember that setPosition of a node takes all three axis setPosition(x,y,z)
Animation.prototype.onUpdate = function onUpdate () {
// while the transitionable is still transitioning
// keep requesting updates
if (this.xPosition.isActive()) {
// set the position of the component's node
// every frame to the value of the transitionable
this.node.setPosition(this.xPosition.get(), 0, 0);
this.node.requestUpdateOnNextTick(this.id);
}
};
I'm using Google Maps OverlayView class to create custom markers (with Raphael JS) and am having issues accessing certain properties of my new subclass when calling a public method.
I followed Google's fairly straightforward example here ~ https://developers.google.com/maps/documentation/javascript/overlays#CustomOverlays ~ to create a custom marker class, including its 'hide' and 'show' methods.
function MapCustomMarker(opts){
this.pos_ = opts.position;
this.map_ = opts.map;
this.div_ = null;
this.color_ = (!opts.color ? '#e32636' : opts.color);
this.height_ = 32;
this.width_ = 32;
this.scale_ = 1.2;
this.icons_ = {
pinpoint:'M16,3.5c-4.142,0-7.5,3.358-7.5,7.5c0,4.143,7.5,18.121,7.5,18.121S23.5,15.143,23.5,11C23.5,6.858,20.143,3.5,16,3.5z M16,14.584c-1.979,0-3.584-1.604-3.584-3.584S14.021,7.416,16,7.416S19.584,9.021,19.584,11S17.979,14.584,16,14.584z',
help: 'M12.558,15.254c2.362,0,4.277-1.916,4.277-4.279s-1.916-4.279-4.277-4.279c-2.363,0-4.28,1.916-4.28,4.279S10.194,15.254,12.558,15.254zM15.662,15.224c-0.875,0.641-1.941,1.031-3.103,1.031c-1.164,0-2.231-0.391-3.105-1.031c-0.75,0.625-1.498,1.519-2.111,2.623c-1.422,2.563-1.578,5.192-0.35,5.874c0.55,0.312,1.127,0.078,1.723-0.496c-0.105,0.582-0.166,1.213-0.166,1.873c0,2.938,1.139,5.312,2.543,5.312c0.846,0,1.265-0.865,1.466-2.188c0.201,1.311,0.62,2.188,1.462,2.188c1.396,0,2.544-2.375,2.544-5.312c0-0.66-0.062-1.291-0.167-1.873c0.598,0.574,1.174,0.812,1.725,0.496c1.228-0.682,1.069-3.311-0.353-5.874C17.159,16.742,16.412,15.849,15.662,15.224zM19.821,3.711l-1.414,1.414c1.499,1.499,2.428,3.569,2.428,5.851c0,2.283-0.929,4.353-2.428,5.853l1.413,1.412c1.861-1.86,3.015-4.43,3.015-7.265C22.835,8.142,21.683,5.572,19.821,3.711zM16.288,14.707l1.413,1.414c1.318-1.318,2.135-3.138,2.135-5.145c0-2.007-0.816-3.827-2.134-5.145l-1.414,1.414c0.956,0.956,1.547,2.275,1.547,3.731S17.243,13.751,16.288,14.707zM21.941,1.59l-1.413,1.414c2.042,2.042,3.307,4.862,3.307,7.971c0,3.11-1.265,5.93-3.308,7.972l1.413,1.414c2.405-2.404,3.895-5.725,3.895-9.386C25.835,7.315,24.346,3.995,21.941,1.59z'
}
this.popup_ = 'M16,5.333c-7.732,0-14,4.701-14,10.5c0,1.982,0.741,3.833,2.016,5.414L2,25.667l5.613-1.441c2.339,1.317,5.237,2.107,8.387,2.107c7.732,0,14-4.701,14-10.5C30,10.034,23.732,5.333,16,5.333z';
this.icon_ = this.icons_[opts.icon];
this.setMap(opts.map);
}
MapCustomMarker.prototype = new google.maps.OverlayView();
MapCustomMarker.prototype.onAdd = function() {
// Create the DIV and set some basic attributes.
var div = document.createElement('div');
div.style.border = "none";
div.style.borderWidth = "0px";
div.style.position = "absolute";
div.style.cursor = "pointer";
div.style.width = this.width_+"px";
div.style.height = this.height_+"px";
var paper = Raphael(div,this.height_, this.width_);
var el = paper.path(Raphael.transformPath(this.icon_, 's'+this.scale_)).attr({fill: this.color_, stroke: "#333333"});
// Set the overlay's div_ property to this DIV
this.div_ = div;
// We add an overlay to a map via one of the map's panes.
// We'll add this overlay to the overlayImage pane.
var panes = this.getPanes();
panes.overlayMouseTarget.appendChild(div);
}
MapCustomMarker.prototype.draw = function() {
// Size and position the overlay.
var overlayProjection = this.getProjection();
// We'll use these coordinates to position the DIV.
var o = overlayProjection.fromLatLngToDivPixel(this.pos_);
var l = o.x - Math.round(this.width_ / 2);
var t = o.y - this.height_;
this.div_.style.left = l + 'px';
this.div_.style.top = t + 'px';
}
MapCustomMarker.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
MapCustomMarker.prototype.hide = function() {
console.log(this.div_);
console.log(this.color_);
if (this.div_) {
this.div_.style.visibility = "hidden";
}
}
MapCustomMarker.prototype.show = function() {
if (this.div_) {
this.div_.style.visibility = "visible";
}
}
MapCustomMarker.prototype.toggle = function() {
if (this.div_) {
if (this.div_.style.visibility == "hidden") {
this.show();
} else {
this.hide();
}
}
}
This class creates markers on my map with the Raphael icons very nicely.
The problem comes when I want to hide or show any specific marker.
var marker = new MapCustomMarker({position: pos, map: self.map, icon:'help', color:'#e32636'});
marker.hide();
marker.hide() is not hiding the markers.
You'll notice in the "hide" method, I have two console.log commands testing the values of this.color_ and this.div_. console.log(this.color_) returns the color set when the object is initiated. console.log(this.div_) returns null even though it was obviously altered in the 'onAdd' and 'draw' methods when the marker was created.
I'm not sure if this is a misunderstanding of public and private properties in javascript or something else. I used the google maps custom overlay example almost exactly.
If anyone has any ideas, please pass them along. (And aside from this one issue, I hope this code will assist others who want to merge Raphael JS capabilities with Google Maps.)
Thanks!
So it appears that marker.hide() is being called before the methods 'onAdd' and 'draw' have a chance to alter the this.div_ variable when the object is instantiated. Since I want all markers to be hidden when they are created, I'm just going to add
div.style.visibility = "hidden";
to the onAdd method, and will call maker.show() later, well after all of the markers have been loaded.