I have a SVG and I want to apply rotation transformation on it but not just with the mouse drag, instead with a mouse drag while pressing the alt key. I have tried to implement something but things donot seem to work as I want. Below is what i have implemented. How can I get the SVG rotated on dragging the mouse while pressing the alt key?
var width = 590, height = 400;
var svg = d3.select("#sketch4").append("svg")
.attr("width", width)
.attr("height", height)
//.call(d3.zoom().on("zoom", zoomed))
.call(d3.drag().on('drag', dragged))
.append("g")
var rect = svg.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("width", 60)
.attr("height", 30)
.attr("fill", "green")
function dragged() {
if (d3.event.sourceEvent.altKey){
var me = sketchSVG.node()
var x1 = me.getBBox().x + me.getBBox().width/2;
var y1 = me.getBBox().y + me.getBBox().height/2;
sketchSVG.attr("transform","rotate("+d3.event.y+","+x1+","+y1+")" );
metricSVG.attr("transform","rotate("+d3.event.y+","+x1+","+y1+")" );
}
};
You need to use d3.event.sourceEvent.x and d3.event.sourceEvent.y. Changing that should get your code working.
To move/zoom your element, create and else condition and just use sketchSVG.attr("transform",d3.event.transform) for zooming and translating the element.
var width = 590,
height = 400;
var svg = d3.select("#sketch4").append("svg")
.attr("width", width)
.attr('id', 'sketchSvg')
.attr("height", height)
.append("g")
d3.select('#sketchSvg')
.call(d3.zoom().on("zoom", dragged))
var rect = svg.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("width", 60)
.attr("height", 30)
.attr("fill", "green")
function dragged(d) {
if (d3.event.sourceEvent.altKey && d3.event.sourceEvent.type == 'mousemove') {
var me = svg.node()
var x1 = me.getBBox().x + me.getBBox().width / 2;
var y1 = me.getBBox().y + me.getBBox().height / 2;
svg.attr("transform", "rotate(" + d3.event.sourceEvent.y + "," + x1 + "," + y1 + ")");
}
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id='sketch4'></div>
Here's one on JSFiddle
Related
The basic idea is that every time that I click the plane will drop a bomb. The moving plane and the dropping bomb are ok, i have two problems:
1) If I drop multiple bombs every explosion animation starts at the same moment of the previous one. Is there is a way to have a different animation for every dropped bomb?
2) I am trying to use ease("cubic"), but there is some mistake using it so if possible can you share a tip on how to use it well?
let svg = d3.select("svg"),
width = svg.attr("width"),
height = svg.attr("height"),
speed = 0.3,
movement = 600;
let x = 0;
let y = 50;
let w = 100;
let plane = svg.append("svg:image")
.attr("x", x)
.attr("y", y)
.attr("width", 100)
.attr("height", 100)
.attr("xlink:href", "b52js.png");
transition();
svg.on("click", function() {
var bombingx = plane.attr("x")
let bomb = svg.append("svg:image")
.attr("x", bombingx - 2.5 + 50)
.attr("y", y + 50)
.attr("width", 15)
.attr("height", 20)
.attr("xlink:href", "bomb.png");
bomb
.transition()
.duration(1200)
.attr("y", height - 10)
.ease("cubic")
.on("end", function() {
let exp = svg.append("svg:image")
.attr("x", bombingx)
.attr("y", height - 190)
.attr("height", 250)
.attr("width", 150)
.attr("xlink:href", "giphy.gif");
d3.timer(function(elapsed) {
exp.remove()
}, 1500);
bomb.remove();
})
});
Referring to my comment, lets change the xlink:href randomly on every click. Gifs of the same dimensions and short length are preferred. Or just create multiple copies of the same gif and put them in the array.
Here's a fiddle:
let svg = d3.select("svg"),
width = svg.attr("width"),
height = svg.attr("height"),
speed = 0.3,
movement = 600;
let x = 0;
let y = 50;
let w = 100;
let g = svg.append('g').attr('id', 'gg')
let plane = svg.append("svg:image")
.attr("x", x)
.attr("y", y)
.attr("width", 100)
.attr("height", 100)
.attr("xlink:href", "https://pngimg.com/uploads/plane/plane_PNG5243.png");
/* transition(); */
svg.on("click", function() {
var bombingx = plane.attr("x")
let bomb = svg.append("svg:image")
.attr("x", bombingx - 2.5 + 50)
.attr("y", y + 50)
.attr("width", 15)
.attr("height", 20)
.attr("xlink:href", "https://pngimg.com/uploads/bomb/bomb_PNG38.png");
bomb
.transition()
.duration(1200)
.attr("y", height - 10)
.ease("cubic")
.each("end", function() {
let exp = g.append("g:image")
.attr("x", bombingx)
.attr("y", height - 190)
.attr("height", 250)
.attr("width", 150)
.attr("xlink:href", function() {
// Lets create an array of gif links
let gifs = ["https://media.giphy.com/media/xA6evoIAtqSzK/giphy.gif", "https://media.giphy.com/media/rkkMc8ahub04w/giphy.gif", "https://media.giphy.com/media/oe33xf3B50fsc/giphy.gif"]
// A function to return random integers
function randInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
//randInt(3) will return 0,1 or 2.
//This number should be the number of gif links you have in your gif link array.
return gifs[randInt(3)];
});
setTimeout(function() {
exp.remove();
}, 1500);
bomb.remove();
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.13/d3.min.js"></script>
<svg width='300' height='300'></svg>
How can I pan with the mouse wheel using d3.js version 4.
I found this example using v3, but it does not work with v4.
Example link
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<title>D3.js: Panning with mouse wheel</title>
<style>
.overlay {
fill: none;
pointer-events: all;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500;
var randomX = d3.random.normal(width / 2, 80),
randomY = d3.random.normal(height / 2, 80);
var data = d3.range(2000).map(function() {
return [
randomX(),
randomY()
];
});
var zoomer = d3.behavior.zoom()
.on("zoom", zoom)
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.call(zoomer)
.on("wheel.zoom",pan)
.append("g");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height);
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 2.5)
.attr("transform", function(d) { return "translate(" + d + ")"; });
function zoom() {
console.log(d3.select(this))
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function pan() {
current_translate = d3.transform(svg.attr("transform")).translate;
dx = d3.event.wheelDeltaX + current_translate[0];
dy = d3.event.wheelDeltaY + current_translate[1];
svg.attr("transform", "translate(" + [dx,dy] + ")");
d3.event.stopPropagation();
}
</script>
I had the same need and I figured out the changes required in D3v4 (below). I also posted to Blocks here.
var width = 960,
height = 500;
var randomX = d3.randomNormal(width / 2, 80),
randomY = d3.randomNormal(height / 2, 80);
var data = d3.range(2000).map(function() {
return [
randomX(),
randomY()
];
});
var zoomer = d3.zoom().scaleExtent([1 / 2, 4]).on("zoom", zoom)
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g");
svg.call(zoomer)
.on("wheel.zoom", null)
.on("wheel", pan);
g.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height);
g.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 2.5)
.attr("transform", function(d) { return "translate(" + d + ")"; });
function zoom() {
g.attr("transform", d3.event.transform);
}
function pan() {
zoomer.translateBy(svg.transition().duration(100), d3.event.wheelDeltaX, d3.event.wheelDeltaY);
}
.overlay {
fill: none;
pointer-events: all;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.12.2/d3.min.js"></script>
<title>D3.js v4: Panning with mouse wheel</title>
<body></body>
I am working on dragging and dropping svg using d3js. There are two problems and I think they are related to each other.
When the circle is dropped it has to detect that it was dropped into the rectangle. Some of the examples that I have looked at uses x and y coordinates of the mouse, but I don't fully understand it.
Another problem is that the circle appears behind the rectangle. Is there a way to bring it to the front when the circle is moving around without changing the order of where the circle and rectangle are created i.e(create circle first and then rectangle).
var width = window.innerWidth,
height = window.innerHeight;
var drag = d3.behavior.drag()
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
//create circle and space evenly
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var circle = d3.select("svg")
.append("circle")
.attr("cx", 50)
.attr("cy", 30)
.attr("r", 15)
.attr("transform", "translate(0,0)")
.style("stroke", "black")
.call(drag);
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
}
function dragged(d) {
d3.select(this).attr("transform", "translate(" + [d3.event.x, d3.event.y] + ")");
}
function dragended(d) {
d3.event.sourceEvent.stopPropagation();
// here would be some way to detect if the circle is dropped inside the rect.
}
var ellipse = svg.append("rect")
.attr("x", 150)
.attr("y", 50)
.attr("width", 50)
.attr("height", 140)
.attr("fill", "green");
<script src="https://d3js.org/d3.v3.min.js"></script>
Any help is appreciated.
Updated to still include the bounding client rectangle, but iterate through any number of rectangles that exist. New Fiddle here.
Here's my solution to the problem. I used a great little "moveToBack" helper function seen here to move the rect to the back without changing the order in which it appears.
To get the positions of the circle and rectangle, I made heavy use of the vanilla js getBoundingClientRect() method. You can see all this together in this JS Fiddle.
var width = window.innerWidth,
height = window.innerHeight;
var drag = d3.behavior.drag()
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
//create circle and space evenly
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var circle = d3.select("svg")
.append("circle")
.attr("r", 15)
.attr("transform", "translate(50,30)")
.style("stroke", "black")
.attr("id", "circle")
.call(drag);
d3.selection.prototype.moveToBack = function() {
return this.each(function() {
var firstChild = this.parentNode.firstChild;
if (firstChild) {
this.parentNode.insertBefore(this, firstChild);
}
});
};
var rect = svg.append("rect")
.attr("x", 150)
.attr("y", 50)
.attr("width", 50)
.attr("height", 140)
.attr("fill", "green")
.attr("id", "rect")
.moveToBack();
var rect2 = svg.append("rect")
.attr("x", 350)
.attr("y", 50)
.attr("width", 50)
.attr("height", 140)
.attr("fill", "green")
.attr("id", "rect")
.moveToBack();
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
}
function dragged(d) {
d3.select(this).attr("transform", "translate(" + d3.event.x + "," + d3.event.y + ")");
}
function dragended(d) {
// Define boundary
var rects = document.querySelectorAll("rect");
for (var i = 0; i < rects.length; i++) {
var rectDimensions = rects[i].getBoundingClientRect();
var xmin = rectDimensions.x;
var ymin = rectDimensions.y;
var xmax = rectDimensions.x + rectDimensions.width;
var ymax = rectDimensions.y + rectDimensions.height;
// Get circle position
var circlePos = document.getElementById("circle").getBoundingClientRect();
var x1 = circlePos.x;
var y1 = circlePos.y;
var x2 = circlePos.x + circlePos.width;
var y2 = circlePos.y + circlePos.height;
if(x2 >= xmin && x1 <= xmax && y2 >= ymin && y1 <= ymax) {
rects[i].setAttribute("fill", "red");
} else {
rects[i].setAttribute("fill", "green");
}
}
d3.event.sourceEvent.stopPropagation();
}
Unable to drag rectangle in d3js. The code looks fine to me. What I am I missing here?
All the drag behaviors like start drag and drag end are implemented but still the drag is not working
Here is my code:
var svgContainer = d3.select("body").append("svg")
.attr("width", 800)
.attr("height", 803);
var rect = svgContainer.append("rect")
.attr("x", 10)
.attr("y", 50)
.attr("width", 51)
.attr("height", 41)
.attr("stroke", "#7E7E7E")
.attr("cursor", "move")
var drag = d3.behavior.drag()
.on("dragstart", dragstart)
.on("drag", drag)
.on("dragend", dragend);
function dragstart()
{
d3.event.sourceEvent.stopPropagation();
}
function drag()
{
var self = d3.select(this);
//var translate =d3.transform(self.getAttribute("transform")).translate;
var translate = d3.transform(self.attr("transform")).translate;
var x = d3.event.dx + translate[0],
y = d3.event.dy + translate[1];
self.attr("transform", "translate(" + x + "," + y + ")");
}
function dragend()
{
var mouseCoordinates = d3.mouse(this);
if (mouseCoordinates[0] > 170)
{
//Append new element
svg.append("g").append("rect")
.attr("x", mouseCoordinates[0])
.attr("y", mouseCoordinates[1])
.attr("width", 51)
.attr("height", 41)
.attr("stroke", "#7E7E7E")
.style('cursor', 'move')
.classed("initialDragRect", true)
.call(drag);
}
}
rect.call(drag);
Change the translate variable declaration on your code in function drag into this
var translate = d3.transform(self.attr("transform")).translate;
The problem is because the self variable does not have the function getAttribute()
I want to achieve followings,
1 - Create a given column matrix with rectangles with provided colours
Done
2 - Make this matrix zoom able
3 - Add list items to each rectangle which will only show numbers of list items in it if completely zoomed out and on zoom in, it will show the list items e.g. there Titles.
Now I want to achieve Number 2 here, this is what I am trying,
http://jsfiddle.net/nhe613kt/25/
When I add code for zooming it fails,
var svgContainer = d3.select("body").append("svg")
.attr("width", 300)
.attr("height", 300)
.style("background-color", "black");
var zoomed = function () {
svgContainer.attr("transform", "translate("+ d3.event.translate + ")
scale(" + d3.event.scale + ")");
};
var zoom = d3.behavior.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed);
.size([width, height]);
var rectangle1 = svgContainer.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red")
.call(zoom);;
var rectangle2 = svgContainer.append("rect")
.attr("x", 100)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "yellow");
var rectangle3 = svgContainer.append("rect")
.attr("x", 200)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red");
var rectangle4 = svgContainer.append("rect")
.attr("x", 0)
.attr("y", 100)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "yellow");
var rectangle5 = svgContainer.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red");
var rectangle6 = svgContainer.append("rect")
.attr("x", 200)
.attr("y", 100)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "yellow");
var rectangle7 = svgContainer.append("rect")
.attr("x", 0)
.attr("y", 200)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red");
var rectangle8 = svgContainer.append("rect")
.attr("x", 100)
.attr("y", 200)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "yellow");
var rectangle9 = svgContainer.append("rect")
.attr("x", 200)
.attr("y", 200)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red");
My desired result will be this,
http://bl.ocks.org/mbostock/3680957
The code you provided has several problems:
1. There is a syntax error in definition of zoom (.on("zoom", zoomed);)
2. You haven't defined width and height.
3. zoomed function possibly couldn't be parsed because of wrong line breaks (notice point where you define scale of transformation).
Here is JSFiddle, where zoom works correctly for first element of matrix. Main points is:
// don't forget about width and height
var width = 960,
height = 500;
// make sure that string defining transform attribute is correct. scale isn't a method, but part of string
var zoomed = function () {
svgContainer.attr("transform", "translate("+ d3.event.translate + ")scale(" + d3.event.scale + ")");
};
// don't place semicolon after on("zoom", zoomed)
var zoom = d3.behavior.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed)
.size([width, height]);
// add zoom behaviour to desired rectangle
var rectangle1 = svgContainer.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "red")
.call(zoom);