I'm a beginner at GSAP.
I have a complex SVG which runs perfectly in HTML. I'm trying to convert it into React by using GSAP.
How can I convert the HTML SVG in react?
Here's the link to HTML SVG: https://codesandbox.io/s/demo-svg-html-esf3dc?file=/index.html
While you put hover over the circle it is animated.
Here's the Link to my React App: https://codesandbox.io/s/framer-motion-svg----3333-zcvdk1?file=/src/components/MainSVG.js
I try to put all curves parents' id in the motion path. I got an error. Now as you can see I just put only 1 path id in the motion path and all works like a mess.
Here's a JS function but I don't know where and how to add that in react. Maybe if I add that to my code it will work.
const existElementId = (e) => {
return document.getElementById(e)
}
existElementId("circle" + 1)
for (let i = 1; null != existElementId("circle" + i); i++) {
console.log(existElementId("circle" + i))
let tl = gsap.timeline({ repeat: -1 });
tl.to("#dot" + i, {
duration: document.querySelectorAll("#curve" + i + " path")[0].getTotalLength() / 200,
ease: "none",
motionPath: {
path: "#curve" + i + " path",
align: "#curve" + i + " path",
alignOrigin: [0.5, 0.5],
}
});
tl.pause()
existElementId("circle" + i).onmouseover = () => {
tl.play()
}
existElementId("circle" + i).onmouseleave = () => {
tl.pause().time(0)
}
}
I'm expecting to get any solution/idea to make it like the index.html file.
Related
I have a GUI that shows live data of little icons moving around based on the data from my postgresql database. When I click on one of the icons, a popup window shows with the data from the database. However, it only shows data on the time the icon was clicked and I want the data displayed to update as the database updates. I could really use some help.
The Popup Window Event
addClickEvent(id, source, publish) {
this.map.on('click', id, function (e) {
let description = ''
let trackId = e.features[0].properties.id
let trackSource = e.features[0].properties.track_source
let trackIdWithSource = trackId + '_' + source
//HELPFUL TIP: This will need to be updated when you bring over an update to base with the previous version
for (const [key, value] of Object.entries(e.features[0].properties)) {
if (isLowerCase(key)) {
}
else { description += key + ': ' + value + '\n' }
}
let trackWindow = document.getElementById("a" + trackIdWithSource);
if (!trackWindow) {
let trackDescription = document.createElement('div')
trackDescription.id = "a" + trackIdWithSource
trackDescription.innerHTML = description;
trackDescription.style.whiteSpace = "pre-wrap"
document.getElementById('root').appendChild(trackDescription)
new WindowModal({ size: { x: 'max-content', y: 200 }, pos: { x: 300, y: 25 }, title: trackIdWithSource + " Info: ", elementSelector: '#a' + trackIdWithSource })
}
trackWindow = document.getElementById("a" + trackIdWithSource);
//let trackContent = trackWindow.getElementsByClassName("WindowModal-content")
if (publish) {
if (!document.getElementById('publish_' + trackIdWithSource)) {
let publishButton = document.createElement('button')
publishButton.innerHTML = "Publish"
publishButton.id = "publish_" + trackIdWithSource
publishButton.onclick = function () {
let url = process.env.REACT_APP_PROTOCOL + '://' + process.env.REACT_APP_PUBLISH_API_HOST + ':' + process.env.REACT_APP_PUBLISH_API_PORT + '/track/' + trackId + '&' + trackSource + '/publish'
fetch(url, { method: 'post' })
.then(function (data) {
return console.log(data.status);
})
}
publishButton.className = "button"
trackWindow.insertBefore(publishButton, trackWindow.children[1])
}
}
});
const popup = new maplibregl.Popup({
closeButton: false,
closeOnClick: false
});
this.map.on("mouseenter", id, function (e) {
const coordinates = e.features[0].geometry.coordinates.slice();
let description = e.features[0].properties.id;
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
popup.setLngLat(coordinates)
.setHTML(description)
.addTo(this);
});
this.map.on('mouseleave', id, () => {
this.map.getCanvas().style.cursor = '';
popup.remove();
});
}
My organisation uses the Erddap data server to publish oceanographic models. We also use Leaflet to publish web maps and are looking at using the Leaflet Velocity library to create visualisations of modelled sea surface velocity.
The code below creates something that looks like the (not well documented) JSON format expected by Leaflet Velocity, and it seems to parse as I get no errors in my JavaScript Console - but nothing is displaying on the map.
Anyone have any ideas?
const minLat = 48.5;
const maxLat = 58.5;
const minLon = -17.0;
const maxLon = -2.0;
const refTime = '2019-12-15T00:00:00Z';
const spacingLat = 0.025;
const spacingLon = 0.025;
const strideLon = 20;
const strideLat = 20;
const strideTime = 1;
var mymap = L.map('map').setView([maxLat-((maxLat-minLat)/2), maxLon-((maxLon-minLon)/2)], 7);
var gebco = L.tileLayer.wms('https://www.gebco.net/data_and_products/gebco_web_services/2019/mapserv?',{layers: 'gebco_2019_grid'}).addTo(mymap);
fetch('https://erddap.marine.ie/erddap/griddap/IMI_Model_Stats.json?sea_surface_x_velocity[(' + refTime + '):1:(' + refTime + ')][(' + String(minLat) + '):' + String(strideLat) + ':(' + String(maxLat) + ')][(' + String(minLon) + '):'+ String(strideLon) +':(' + String(maxLon) + ')],sea_surface_y_velocity[(' + refTime + '):1:(' + refTime + ')][(' + String(minLat) + '):' + String(strideLat)+ ':(' + String(maxLat) + ')][(' + String(minLon) + '):' + String(strideLon) + ':(' + String(maxLon) + ')]').
then(
response => response.json()
).
then(
data => [{
'header':{
'la1': maxLat,
'la2': minLat,
'lo1': minLon,
'lo2': maxLon,
'dx': strideLon * spacingLon,
'dy': strideLat * spacingLat,
'nx': [...new Set(data.table.rows.map(x => x[2]))].length,
'ny': [...new Set(data.table.rows.map(x => x[1]))].length,
'parameterCategory': 2,
'parameterNumber': 2,
'parameterUnit': 'm.s-1',
'parameterNumberName': data.table.columnNames[3],
'refTime': refTime.replace('T', ' ').replace('Z','')
},
'data': data.table.rows.map(x => x[3])
},{
'header':{
'la1': maxLat,
'la2': minLat,
'lo1': minLon,
'lo2': maxLon,
'dx': strideLon * spacingLon,
'dy': strideLat * spacingLat,
'nx': [...new Set(data.table.rows.map(x => x[2]))].length,
'ny': [...new Set(data.table.rows.map(x => x[1]))].length,
'parameterCategory': 2,
'parameterNumber': 3,
'parameterUnit': 'm.s-1',
'parameterNumberName': data.table.columnNames[4],
'refTime': refTime.replace('T', ' ').replace('Z','')
},
'data': data.table.rows.map(x => x[4])
}]
).then(
report => {
//document.getElementById('dump').innerHTML = JSON.stringify(report);
try{
var velocityLayer = L.velocityLayer({
displayValues: true,
displayOptions: {
velocityType: "GBR Water",
displayPosition: "bottomleft",
displayEmptyString: "No water data"
},
data: report,
maxVelocity: 0.3,
minVelocity: 0,
velocityScale: 0.1,
opacity: 0.97
}).addTo(mymap);
document.getElementById('dump').innerHTML = JSON.stringify(report);
console.log(report);
} catch(err) {
document.getElementById('err').innerHTML = err.message;
}
}
)
In the end, I sorted this out and am leaving the link here to a working example of the code
https://irishmarineinstitute.github.io/erddap-leaflet-velocity-demo/
https://github.com/IrishMarineInstitute/erddap-leaflet-velocity-demo
I have 2 vector layers of which I want only 1 to be selectable for a WFS get feature info layer. OL4 docs tell me there is an opt_layerfilter for the forEachFeatuerAtPixel function.
I’m in a similar situation like this: OpenLayers 3 hasFeatureAtPixel filter for layer.
Due to my lack of JavaScript knowledge I can’t seem to make it work with the following code in OpenLayers 4:
var displayFeatureInfo = function (pixel) {
var features = [];
map.forEachFeatureAtPixel(pixel, {
layerFilter: function (layer) {
return layer.get('name') === 'isochrones';
}
}, function (feature) {
features.push(feature);
});
if (features.length > 0) {
var info = [];
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
info.push('<div id="infobox">' + '<p2>' + 'Isochroon ' + features[i].get('name') + ', locatie ' + features[i].get('facilityid') + '</p2>' + '<p>' + 'aantal lopend: ' + features[i].get('n_pedestrians') + ', fiets: ' + features[i].get('n_bike') + ', ebike: ' + features[i].get('n_ebike') + '<br>' + 'speedpedelec: ' + features[i].get('n_speedpedelec') + ', auto: ' + features[i].get('n_car') + '</p>' + '</div>');
}
document.getElementById('info').innerHTML = info.join(', ') || ' ';
} else {
document.getElementById('info').innerHTML = ' ';
}
};
map.on('click', function (evt) {
displayFeatureInfo(evt.pixel);
});
The layer I want to be selectable is named ‘isochrones’.
It throws me an error “d.call is not a function” when I try to click any vector layer in the map.
Could anyone point me in the right direction?
Looks like you have your args swapped.
The params for forEachFeatureAtPixel are (pixel, callback, options)
You have (pixel, options, callback)
I define several kineticJS circles in an object and create and display several of those objects. What I want is to generate an XML object based on the ones the user selects out of the displayed circles.
For this I tried to set a variable value defined in the object when clicked on a particular circle and call it by an object.property kind of a way. However, this doesn't work.
Following is some of the code I used for the job:
function Graph(i, j, refRel, refRelTxt) {
subNodes = seventeen + eighteen + nineteen;
graphNode = "<" + refRelTxt + ">" + subNodes + "<" + refRelTxt + ">";
var layer = new Kinetic.Layer({
width: 200,
height: 100,
x: (stage.getWidth() / 2.5) + i,
y: (stage.getHeight() / 2.5) + j
});
var circle19 = new Kinetic.Circle({
x: 10,
y: layer.getHeight() / 2,
radius: cradius,
fill: '#CCCCCC'
});
//++++++++++++++++++++++++++++++++++++++++++++++++++
var circle18 = new Kinetic.Circle({
x: 10 + n,
y: (layer.getHeight() / 2) - m,
radius: cradius,
fill: '#CCCCCC'
});
var circle17 = new Kinetic.Circle({
x: 10 + n,
y: (layer.getHeight() / 2) + m,
radius: cradius,
fill: '#CCCCCC'
});
//============Mouse in/out operations
circle17.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
seventeen = "<node>seventeen<node>";
});
circle18.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
eighteen = "<node>eighteen<node>";
});
circle19.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
nineteen = "<node>nineteen<node>";;
});
// add the shape to the layer
layer.add(circle19);
layer.add(circle18);
layer.add(circle17);
stage.add(layer);
}
Create some graph objects like the following:
var graph19 = new Graph(-(4*hdist), (0*vdist),"circle19","nineteen");
And then call the graph19.graphNode value to get the xml out based on the circles the user has clicked. However, graph19.graphNode doesn't get set with the values dynamically. I know I am doing something wrong here that I cannot figure out. When I set global variables and do this it works. What I want is to extract the xmls form the various Graph objects that are created.
If I understand correctly... you want to be able to call graph19.graphNode and it will output some pre-defined xml string that is dynamically populated depending on which circles the user clicked.
If so:
First off, to be able to call the property graphNode from a new Graph object, you have to assign this.graphNode instead of just graphNode by itself. Otherwise when you call graph19.graphNode it will be undefined.
Then you need to assign this to a variable like: var node = this; so that you can access node within the click function. (This is because by default, when you use this inside a Kinetic event function, click in this case, this will be assigned to the Kinetic.Shape you are binding the event to)
Finally, you need to update node (this.graphNode) every time you click a circle, use an array to keep track of the elements selected:
this.graphNode = '';
var node = this;
var nodeText = '';
circle17.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
seventeen = "<node>seventeen<node>";
nodeText += seventeen;
node.graphNode = "<" + refRelTxt + ">" + nodeText + "<" + refRelTxt + ">";
});
circle18.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
eighteen = "<node>eighteen<node>";
nodeText += eighteen;
node.graphNode = "<" + refRelTxt + ">" + nodeText + "<" + refRelTxt + ">";
});
circle19.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
nineteen = "<node>nineteen<node>";
nodeText += nineteen;
node.graphNode = "<" + refRelTxt + ">" + nodeText + "<" + refRelTxt + ">";
});
The reason your code wasn't working was because you've defined
subNodes = seventeen + eighteen + nineteen;
Before seventeen eighteen and nineteen have even been defined (since they are defined when the user clicks on the the respective Kinetic.Circle
Also, as a suggestion I recommend that instead of creating a new Kinetic.Layer and adding it to the stage every time you call your Graph function, you might want to create a new Kinetic.Group instead and add that to the layer. See here for more information: What are the differences between group and layer in KineticJs
UPDATE
Okay if you need to deselect the objects as well, we'll need to keep track of which objects are selected in an array. Then we can use that array to determine how this.graphNode will be populated.
Something like this (note I haven't tested this code..):
this.graphNode = '';
this.selectedNodes = [];
this.nodeText = '';
var node = this;
circle17.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
seventeen = "<node>seventeen<node>";
selectedNodes.push(seventeen);
node.nodeText = '';
for (var i=0; i<selectedNodes.length; i++) {
node.nodeText += selectedNodes[i];
}
node.graphNode = "<" + refRelTxt + ">" + node.nodeText + "<" + refRelTxt + ">";
});
circle18.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
eighteen = "<node>eighteen<node>";
selectedNodes.push(eighteen);
node.nodeText = '';
for (var i=0; i<selectedNodes.length; i++) {
node.nodeText += selectedNodes[i];
}
node.graphNode = "<" + refRelTxt + ">" + node.nodeText + "<" + refRelTxt + ">";
});
circle19.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
nineteen = "<node>nineteen<node>";
selectedNodes.push(nineteen);
node.nodeText = '';
for (var i=0; i<selectedNodes.length; i++) {
node.nodeText += selectedNodes[i];
}
node.graphNode = "<" + refRelTxt + ">" + node.nodeText + "<" + refRelTxt + ">";
});
After this you can do something similar to remove the object from the selectedNodes array on dblclick event.
I'm trying to accomplish what seems like a simple task using Ember. I want to drop some images in a directory and have my ember app display those images. A solution I came up with, which I thought was quite clever, was to have a for loop generate a fixture data object, which would correspond to the images in a directory if they were sequentially numbered.
I seem to be getting close, but I am getting this error:
Uncaught Error: the id property must be defined for fixture "{ id: 1, href: \"public/1.jpg\", style: \"top: 0px; left: 0px\" }"
Which seems odd, because there is an id showing up in the excerpted fixture data. This is making me think maybe it's an issue in the way the data is being generated? Here is the full code I'm working with:
//CONFIG
var imgsLength = 3
//JS-PREP
var imgLinks = [];
for (i = 0, topvar = 0, left = 0 ; i<imgsLength ; i++, topvar += 10, left += 10) {
imgLinks.push('{ id: ' + (i + 1) + ', href: "public/' + (i + 1) + '.jpg", style: "top: ' + topvar + 'px; left: ' + left + 'px" }');
}
//APP
App = Ember.Application.create({});
App.Store = DS.Store.extend({
revision: 12,
adapter: DS.FixtureAdapter
});
App.Router.map(function() {
this.resource('images', function() {
this.resource('image', { path: ':image_id' });
});
});
App.ImagesRoute = Ember.Route.extend({
model: function() {
return App.Image.find();
}
});
var attr = DS.attr;
App.Image = DS.Model.extend({
href: attr('string'),
style: attr('string'),
});
App.Image.FIXTURES = imgLinks;
and the relevant HBS code:
{{#each model}}
{{#linkTo image this}}<div {{bindAttr style="style"}}><img {{bindAttr src="href"}}></div>{{/linkTo}}
{{/each}}
Thoughts?
This is making me think maybe it's an issue in the way the data is being generated?
You are guessing right it's the way you are generating the fixtures that causes the problem. So try to do it like this instead:
for (i = 0, topvar = 0, left = 0 ; i<imgsLength ; i++, topvar += 10, left += 10) {
var image = {};
image.id = (i + 1);
image.href = "public/" + (i + 1) + ".jpg";
image.style = "top: " + topvar + "px; left: " + left + "px";
imgLinks.push(image);
}
You should also put the route you are linking to into quotes:
{{#each model}}
{{#linkTo 'image' this}}<div {{bindAttr style="style"}}><img {{bindAttr src="href"}}</div>{{/linkTo}}
{{/each}}
See here working jsbin.
Hope it helps.