I am looking for a way to create a circle with a gradient fill in Leaflet.
My approach so far is to define the fillColor of the circle as 'url(#gradient)' and add the Gradient definition manually via the following code:
function createGradient (map) {
// Get the SVG element from the overlayPane
var svg = map.getPanes().overlayPane.firstChild;
var doc = document.implementation.createDocument(null, null, null);
// Create def element
var svgDef = doc.createElement('defs');
// Create gradient and stops element
var svgGradient = doc.createElement("radialGradient");
var svgStart = doc.createElement('stop');
var svgStop = doc.createElement('stop');
// Set ID attribute of gradient
svgGradient.setAttribute('id', 'gradient');
// set stops and colors
svgStart.setAttribute('offset', '0%');
svgStop.setAttribute('offset', '100%');
svgStart.setAttribute('class', 'circle-start');
svgStop.setAttribute('class', 'circle-stop');
svgGradient.appendChild(svgStart);
svgGradient.appendChild(svgStop);
// Append blur element to filter element
svgDef.appendChild(svgGradient);
// Append filter element to SVG element
svg.appendChild(svgDef);
}
The funny thing is, that the gradient fill is not shown initially. However, if I go into devtools and remove the 'defs' block and add it again it the gradient fill is shown correctly.
Can anyone help me to get rid of this issue or alternatively another way to get a gradient fill?
You cannot create SVG elements using createElement, that's only for html elements. If you want to create an SVG element you must create an element in the SVG namespace using createElementNS i.e.
var svgDef = doc.createElementNS('http://www.w3.org/2000/svg', 'defs');
// Create gradient and stops element
var svgGradient = doc.createElementNS("http://www.w3.org/2000/svg", "radialGradient");
var svgStart = doc.createElementNS('http://www.w3.org/2000/svg', 'stop');
var svgStop = doc.createElementNS('http://www.w3.org/2000/svg', 'stop');
Using devtools reruns the html parser on the content which magically corrects the namespace.
Related
This is probably the best way, but I want to embed an external SVG and move it into position. I'm using the following code to load the external SVG image, but I can't seem to apply any transformation to the fragment.
var svg = new Snap('#svg');
Snap.load('logo.svg', function (fragment) {
svg.append(fragment);
});
It's not clear what you are really trying to transform, as you don't have any transform method in your snippet of code.
The normal route, is to append the fragment to a group element, and put the transform on that (as the svg element itself doesn't support transforms). So something like...
var svg = new Snap('#svg');
var g = svg.g();
Snap.load('logo.svg', function (fragment) {
g.append(fragment);
g.transform('t100,100');
});
I want to change the color of all my svg elements with class "tmp-click" to yellow
var yellow = d3.select('svg')
.selectAll('.tmp-click')
.attr("fill","yellow);
When executing the code only one of the elements is changed, what did I miss?
Do it this way
var yellow = d3.selectAll('.tmp-click')
.style("fill","yellow");
Im using snap.svg an snap.svg.zpd libraries. Same issue I have if I use snap.svg and jQuery panzoom library combination.
Code sample you can find here.
var mySvg = $("#plan")[0];
var snap = Snap("#plan");
//create an image
var imagePlan = snap.image("http://upload.wikimedia.org/wikipedia/commons/4/42/Cathedral_schematic_plan_fr_vectorial.svg", 10, 10, 900, 500);
var group = snap.group(imagePlan);
snap.zpd();
var pt = mySvg.createSVGPoint(); // create the point;
imagePlan.click(function(evt)
{
console.log(evt);
pt.x = evt.x;
pt.y = evt.y;
console.log(mySvg.getScreenCTM().inverse());
//When click, create a rect
var transformed = pt.matrixTransform(mySvg.getScreenCTM().inverse());
var rect1 = snap.rect(transformed.x, transformed.y, 40, 40);
group.add(rect1);
});
Problem is...if you click on initial svg it will add rectangle to the mouse position. If you pan/zoom image and then add rectangle it will be shiffted.
It looks like problem is in method mySvg.getScreenCTM().inverse(). Matrix returned is always same one, panning and zooming does not change it. It always use matrix from initialy rendered svg. However, if I inspect svg element, I can see that pann/zoom change transform matrix directly on element (image below).
Does anybody know how to fix this. My requirement is to be able to drag and drop elements outside svg into svg on any zoom scale or pan context, so I need transformation from mouse click point to svg offset coordinates. If you know any other approach or any other library combination that could done this, it would be ok for me.
Thanks in advance.
Problem is, the transform isn't in mySvg. Its on the 'g' group element thats inside the svg. Zpd will create a group to operate on as far as I know, so you want to look at that.
To hightlight this, take a look at
console.log(mySvg.firstElementChild.getScreenCTM().inverse());
In this case its the g element (there's more direct ways of accessing it, depending on whether you want to just work in js, or snap, or svg.js).
jsfiddle
Its not quite clear from your description where you want the rect (within the svg, separate or whatt) to go and at what scale etc though, and if you want it to be part of the zoom/panning, or static or whatever. So I'm not sure whether you need this or not.
I'm guessing you want something like this
var tpt = pt.matrixTransform( mySvg.firstElementChild.getScreenCTM().inverse() )
var rect1 = snap.rect(tpt.x, tpt.y, 40, 40);
I would like to apply a filter in the overlayImage. the only way to do it, is appling the filter in the whole canvas after render?
There's no built-in support for this, but it's fairly easy to "hack".
var overlayImageUrl = '...';
// load overlay image first
fabric.Image.fromURL(overlayImageUrl, function(oImg) {
// add and apply filter to overlay image
oImg.filters.push(new fabric.Image.filters.Grayscale());
oImg.applyFilters();
// set <img> element of fabric.Image instance
// and assign it directly to canvas' "overlayImage"
canvas.overlayImage = oImg.getElement();
// render canvas for overlayImage to appear
canvas.renderAll();
});
I'm using Raphael.js to draw images to canvas. I need to be able to select certain image elements (this I can do) and make them look like they are selected (this is the problem).
Before Raphael.js I used regular Html5 canvas and simple rectangles. It was simple to delete selected rectangle and draw a new one with a different color to that same place.
But now that I'm using images, it's a different story. The image I'm using is here. It's a small gif.
So the question(s):
Is there a simple way to change color of a Raphael.js image-element programmatically?
Can I make an image-element to blink by changing its opacity?
Only requirement is that the selected element must be movable.
Code for drawing image when user clicks on canvas:
var NodeImage = RCanvas.image("../vci3/images/valaisin.gif", position.x, position.y, 30, 30);
NodeImage.toFront();
RSet.push(NodeImage);
NodeImage.node.id = 'lamp';
NodeImage.node.name = name;
NodeImage.click(function() {
console.log("Clicked on node " + NodeImage.node.name);
// Here should be the code that blinks or changes color or does something else
});
Is this completely bad idea? Is there a better way to achieve my goal?
i would suggest granting the image with an opacity of some level, and assign a value of 1 to it upon click:
NodeImage.attr('opacity', 0.6);
// ...
NodeImage.click(function() {
this.attr('opacity', 1);
});
of course, you will probably want to manage the shape's selected state, to switch the selected style off later on. in fact, you'll want to manage all selectable shapes in the same manner, so let's do that:
// keep all selectable shapes in a group to easily manage them
var selectableShapesArray = [NodeImage, otherNodeImage, anotherSelectableShape];
// define the behavior for shape click event
var clickHandler = function() {
for (var i in selectableShapesArray) {
var image = selectableShapesArray[i];
if (image.selected) {
image.attr('opacity', .6);
image.selected = false;
break;
}
}
this.attr('opacity', 1);
this.selected = true;
}
// attach this behavior as a click handler to each shape
for (var i in selectableShapesArray) {
var shape = selectableShapesArray[i];
shape.click(clickHandler);
}