Raphael-js drag and click events inaccurate after browser resize - resize

I've created a radial dial using the Raphael-js library and it works as should on page load. It's embedded in a responsive layout so I want it to resize according to it's container, which it does. However, the new container size makes the mouse events inaccurate. When I resize it back to what it was on page load, it works fine.
function RadialDial(paperId, opts) {
var thisObj = this;
this.dialParent = document.querySelector(paperId);
this.divPaper = this.dialParent.querySelector('.radialDial');
this.divPaperW = this.divPaper.clientWidth;
this.scaleRatio = this.divPaperW / 250;
this.outputEle = this.dialParent.querySelector('.dialOutput .val');
this.btnPlus = this.dialParent.querySelector('.btnPlus');
this.btnMinus = this.dialParent.querySelector('.btnMinus');
this.debug = this.dialParent.querySelector('.debug');
this.opts = {
dialCenter: this.divPaperW / 2,
dialRadius: this.divPaperW / 2,
startA: 155,
endA: 25,
arcCentralA: 230,
maxRange: 12,
minRange: 3,
postText: false,
rangeSteps: 3
}
this.currNeedleA;
this.rangeAngles = [];
this.setOptions(opts);
this.paper = Raphael(this.divPaper, this.opts.dialRadius * 2, this.opts.dialRadius * 2);
this.rangeDivisions = Raphael.rad(this.opts.arcCentralA / (this.opts.maxRange - this.opts.minRange));
this.arcStartX = (this.opts.dialCenter + ((this.opts.dialRadius - (30 * this.scaleRatio)) * Math.cos(Raphael.rad(this.opts.startA)))).toString();
this.arcStartY = (this.opts.dialCenter + ((this.opts.dialRadius - (30 * this.scaleRatio)) * Math.sin(Raphael.rad(this.opts.startA)))).toString();
var currSectorX = this.arcStartX;
var currSectorY = this.arcStartY;
var dialFaceAtts = (Raphael.svg) ? {fill: "r#ffffff-#ffffff:85-#999999:75-#cccccc:57-#999999", stroke: "none"} : {fill: "#ffffff", stroke: "#999999", "stroke-width": (1 * this.scaleRatio)};
this.dialFace = this.paper.circle(this.opts.dialCenter, this.opts.dialCenter, this.opts.dialRadius).attr(dialFaceAtts);
var dialFaceRim = this.paper.circle(this.opts.dialCenter, this.opts.dialCenter, (102 * this.scaleRatio)).attr({fill: "none", "stroke-width": (8 * this.scaleRatio), stroke: "#eeeeee", "stroke-opacity": 0.4});
var currSectorAngle = Raphael.rad(this.opts.startA);
var rangeSet = this.paper.set();
for (var i = this.opts.minRange; i <= (this.opts.maxRange); i++) {
currSectorX = (this.opts.dialCenter + ((this.opts.dialRadius - (40 * this.scaleRatio)) * Math.cos(currSectorAngle))).toString();
currSectorY = (this.opts.dialCenter + ((this.opts.dialRadius - (40 * this.scaleRatio)) * Math.sin(currSectorAngle))).toString();
if (i % this.opts.rangeSteps == 0) {
var rangeTxt = this.paper.text(currSectorX, currSectorY, i).attr({fill: "#00a2d8", "font-size": (22 * this.scaleRatio).toString()});
rangeSet.push(rangeTxt);
this.rangeAngles[i] = Raphael.deg(this.rangeDivisions * (i - (this.opts.minRange)));
}
currSectorAngle = currSectorAngle + this.rangeDivisions;
}
this.clickArea = this.paper.circle(this.opts.dialCenter, this.opts.dialCenter, this.opts.dialRadius).attr({fill: "red", "fill-opacity": 0, stroke: "none"});
this.needle = this.paper.path("M" + (this.arcStartX).toString() + "," + (this.arcStartY).toString() +
"L" + (this.opts.dialCenter * (138.89401 / this.opts.dialCenter) * this.scaleRatio).toString() + "," + (this.opts.dialCenter * (107.45764 / this.opts.dialCenter) * this.scaleRatio).toString() +
"L" + (this.opts.dialCenter * (147.34637 / this.opts.dialCenter) * this.scaleRatio).toString() + "," + (this.opts.dialCenter * (125.5838 / this.opts.dialCenter) * this.scaleRatio).toString() + "z").attr({fill: '#0058b6', stroke: "none"});/* */
var needleLine = this.paper.path("M" + (this.opts.dialCenter + (18 * this.scaleRatio)).toString() + ' ' + (this.opts.dialCenter - (8 * this.scaleRatio)).toString() + ", L" + this.arcStartX + "," + this.arcStartY).attr({stroke: "#ffffff", "stroke-width": .7});
var centerCircle = this.paper.circle(this.opts.dialCenter, this.opts.dialCenter, (12 * this.scaleRatio)).attr({fill: "#0058b6", stroke: "none"});
this.needleSet = this.paper.set();
this.needleSet.push(this.needle, needleLine);
this.dialSet = this.paper.set();
this.dialSet.push(dialFaceRim, this.dialFace, this.clickArea, this.needleSet, rangeSet, centerCircle, needleLine);
this.paper.setViewBox(0, 0, this.opts.dialRadius * 2, this.opts.dialRadius * 2, true);
this.paper.canvas.setAttribute('preserveAspectRatio', 'none');
this.needleSet.push(this.needle);
this.needleSet.data('thisObj', thisObj);
this.needleSet.data('paperObj', this.paper.canvas);
this.setNeedleDrag();
this.dialFaceClick();
}
RadialDial.prototype = {
constructor: RadialDial,
setOptions: function (opts) {
for (key in opts) {
if (!opts.hasOwnProperty(key)) {
continue;
}
this.opts[key] = opts[key];
}
},
drawDial: function () {
},
elePosition: function (ele) {
var eleX = 0;
var eleY = 0;
while (ele) {
eleX += (ele.offsetLeft - ele.scrollLeft + ele.clientLeft);
eleY += (ele.offsetTop - ele.scrollTop + ele.clientTop);
ele = ele.offsetParent;
}
return {x: eleX, y: eleY};
},
moveNeedle: function (dx, dy, x, y, e) {
var classObj = this.data('thisObj');
var rectObject = classObj.divPaper.getBoundingClientRect();
var paperXY = classObj.elePosition(classObj.divPaper);
var mouseX, mouseY;
mouseX = e.clientX - rectObject.left;
mouseY = e.clientY - rectObject.top;
var needleA = Raphael.angle(classObj.opts.dialCenter, classObj.opts.dialCenter, classObj.needle.getPointAtLength(classObj.needle.getTotalLength())['x'], classObj.needle.getPointAtLength(classObj.needle.getTotalLength())['y']);
var newA = Raphael.angle(classObj.opts.dialCenter, classObj.opts.dialCenter, mouseX, mouseY);
var rotateAngle = (360 - needleA) + newA;
if (!(newA > (360 - classObj.opts.startA) && newA < (360 - classObj.opts.endA))) {
classObj.needleSet.transform('r' + rotateAngle + "," + classObj.opts.dialCenter + "," + classObj.opts.dialCenter);
}
},
setNeedleDrag: function () {
var startDrag = function () {
}, dragger = this.moveNeedle,
endDrag = this.findNearestStep;
this.needleSet.drag(dragger, startDrag, endDrag);
},
dialFaceClick: function () {
var classObj = this;
this.clickArea.node.onclick = function (e) {
var e = e || window.event;
var rectObject = classObj.divPaper.getBoundingClientRect();
var mouseX, mouseY;
mouseX = e.clientX - rectObject.left;
mouseY = e.clientY - rectObject.top;
var needleA = Raphael.angle(classObj.opts.dialCenter, classObj.opts.dialCenter, classObj.needle.getPointAtLength(classObj.needle.getTotalLength())['x'], classObj.needle.getPointAtLength(classObj.needle.getTotalLength())['y']);
var newA = Raphael.angle(classObj.opts.dialCenter, classObj.opts.dialCenter, mouseX, mouseY);
var rotateAngle = (360 - needleA) + newA;
if (!(newA > (360 - classObj.opts.startA) && newA < (360 - classObj.opts.endA))) {
classObj.needleSet.transform('r' + rotateAngle + "," + classObj.opts.dialCenter.toString() + "," + classObj.opts.dialCenter.toString());
}
classObj.findNearestStep(classObj);
return false;
}
},
findNearestStep: function (obj) {
var classObj = (obj.target || obj.srcElement) ? this.data('thisObj') : obj;
var currVal = Math.round((Raphael.rad(classObj.needle.matrix.split().rotate) * ((classObj.opts.maxRange - classObj.opts.minRange) / Raphael.rad(classObj.opts.arcCentralA))) + classObj.opts.minRange);
var nextVal = currVal;
var prevVal, newA, index;
if (currVal % classObj.opts.rangeSteps != 0) {
while (nextVal % classObj.opts.rangeSteps != 0) {
nextVal = nextVal + 1;
}
if ((nextVal - currVal) > (classObj.opts.rangeSteps / 2)) {
nextVal = nextVal - classObj.opts.rangeSteps;
}
index = nextVal;
} else {
index = currVal;
}
newA = classObj.rangeAngles[index];
classObj.needleSet.transform('r' + (newA) + "," + classObj.opts.dialCenter + "," + classObj.opts.dialCenter);
}
}
Here is my fiddle, http://jsfiddle.net/fiddle_fish/rvLo1cuy/ , dragging the needle makes it follow the mouse pointer closely. Now click the "Resize container" link, whilst the needle still moves it doesn't follow the pointer closely. It seems the resize has created an offset for the mouse event target area.
I've tried changing the viewbox settings, width/height values,removing events and reapplying them, deleting the dial on resize and redrawing the dial but nothing works.
Tried, raphael js, calculate setViewBox width height to fix window
and, raphael js, resize canvas then setViewBox to show all elements
Neither works. :(

I sussed this out. I've multiplied the mouse x-y coordinates with a ratio based on the paper size onload/resize. Works a treat :)

I just encountered the same issue. Basically, on window resize I recalculate the "scale" i.e. the ratio of the svg element's viewBox to its current height/width.
Here's my solution:
var scale = {
x:1,
y:1
}
$(window).resize(function(){
scale = getScale(paper);
})
function getScale(paper){
var x = paper.canvas.viewBox.baseVal.width/$(paper.canvas).width();
var y = paper.canvas.viewBox.baseVal.height/$(paper.canvas).height();
return {
x:x,
y:y
}
}
and then in my "move" function, I added a multiplier to dx and dy:
var move = function (dx, dy,x,y) {
var X = this.cx + dx * scale.x,
Y = this.cy + dy * scale.y;
this.attr({cx: X, cy: Y});
}
For context, the move function is used like so:
var start = function(){
this.cx = this.attr("cx"),
this.cy = this.attr("cy");
}, move = function (dx, dy,x,y) {
var X = this.cx + dx * scale.x,
Y = this.cy + dy * scale.y;
this.attr({cx: X, cy: Y});
}
raphael_element.drag(move,start);
Note that there's some JQuery thrown in here that you could easily do without.

Related

Vue js : window.addEventListener ne se trigger pas

My problem is that I am trying to get an access token from Spotify, but my window.addEventListener("message, function() {...}") is never triggered. Refer to the code below:
mounted(){
this.$nextTick(function() {
function login(callback) {
var CLIENT_ID = 'd3ebae5610894ca48c9f66794214252b';
var REDIRECT_URI = 'http://localhost:8080/spotify';
function getLoginURL(scopes) {
console.log('url')
return 'https://accounts.spotify.com/authorize?client_id=' + CLIENT_ID +
'&redirect_uri=' + encodeURIComponent(REDIRECT_URI) +
'&scope=' + encodeURIComponent(scopes.join(' ')) +
'&response_type=token';
}
var url = getLoginURL([
'user-read-email'
]);
var width = 450,
height = 730,
left = (screen.width / 2) - (width / 2),
top = (screen.height / 2) - (height / 2);
var w = window.open(url,
'Spotify',
'menubar=no,location=no,resizable=no,scrollbars=no,status=no, width=' + width + ', height=' + height + ', top=' + top + ', left=' + left
);
window.addEventListener("message", function(event) {
var hash = JSON.parse(event.data);
console.log(JSON.parse(event.data))
if (hash.type == 'access_token') {
callback(hash.access_token);
}
}, false);
}
var loginButton = document.getElementById('btn-login');
loginButton.addEventListener('click', function() {
console.log('click the button')
login(function(accessToken) {
console.log(accessToken)
});
});
})}

D3 pie chart legends with horizontal pagination

I have a requirement where i need to show pagination for legends if the number of legends is more than two. I am using d3 pie chart. Any help would be appreciated.
I have found out something similar but unable to implement using d3 version 5.
I have modified the code you gave in this bl.ocks link.
Here is a link of the codepen I created link.
The pagination implemented here could be directly used with d3 v5. The problem was coming up with the chart.
var legendCount = dataSet.series.length;
var legendWidth = 10;
var legendSpacing = 6;
var netLegendHeight = (legendWidth + legendSpacing) * legendCount;
var legendPerPage, totalPages, pageNo;
// if (netLegendHeight / height > 1) {
legendPerPage = 2;
totalPages = Math.ceil(legendCount / legendPerPage);
pageNo = 1;
var startIndex = (pageNo - 1) * legendPerPage;
var endIndex = startIndex + legendPerPage;
var seriesSubset = [],
colorSubset = [];
for (var i = 0; i < dataSet.series.length; i++) {
if (i >= startIndex && i < endIndex) {
seriesSubset.push(dataSet.series[i]);
colorSubset.push(colors[i]);
}
}
DrawLegendSubset(seriesSubset, colorSubset, legendPerPage, pageNo, totalPages);
// }
function DrawLegendSubset(seriesSubset, colorSubset, legendPerPage, pageNo, totalPages) {
var legend = svg.selectAll("g.legendg")
.data(seriesSubset)
.enter().append("g")
.attr('class', 'legendg')
.attr("transform", function(d, i) {
return "translate(" + (width - 40) + "," + i * (legendWidth + legendSpacing) + ")";
});
legend.append("rect")
.attr("x", 45)
.attr("width", legendWidth)
.attr("height", legendWidth)
.attr("class", "legend")
.style('fill', function(d, i) {
return colorSubset[i];
});
legend.append("text")
.attr("x", 60)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d) {
return d;
});
var pageText = svg.append("g")
.attr('class', 'pageNo')
.attr("transform", "translate(" + (width + 7.5) + "," + (legendPerPage + 1) * (legendWidth + legendSpacing) + ")");
pageText.append('text').text(pageNo + '/' + totalPages)
.attr('dx', '.25em');
var prevtriangle = svg.append("g")
.attr('class', 'prev')
.attr("transform", "translate(" + (width + 5) + "," + (legendPerPage + 1.5) * (legendWidth + legendSpacing) + ")")
.on('click', prevLegend)
.style('cursor', 'pointer');
var nexttriangle = svg.append("g")
.attr('class', 'next')
.attr("transform", "translate(" + (width + 20) + "," + (legendPerPage + 1.5) * (legendWidth + legendSpacing) + ")")
.on('click', nextLegend)
.style('cursor', 'pointer');
nexttriangle.append('polygon')
.style('stroke', '#000')
.style('fill', '#000')
.attr('points', '0,0, 10,0, 5,5');
prevtriangle.append('polygon')
.style('stroke', '#000')
.style('fill', '#000')
.attr('points', '0,5, 10,5, 5,0');
if (pageNo == totalPages) {
nexttriangle.style('opacity', '0.5')
nexttriangle.on('click', '')
.style('cursor', '');
} else if (pageNo == 1) {
prevtriangle.style('opacity', '0.5')
prevtriangle.on('click', '')
.style('cursor', '');
}
}
function prevLegend() {
pageNo--;
svg.selectAll("g.legendg").remove();
svg.select('.pageNo').remove();
svg.select('.prev').remove();
svg.select('.next').remove();
var startIndex = (pageNo - 1) * legendPerPage;
var endIndex = startIndex + legendPerPage;
var seriesSubset = [],
colorSubset = [];
for (var i = 0; i < dataSet.series.length; i++) {
if (i >= startIndex && i < endIndex) {
seriesSubset.push(dataSet.series[i]);
colorSubset.push(colors[i]);
}
}
DrawLegendSubset(seriesSubset, colorSubset, legendPerPage, pageNo, totalPages);
}
function nextLegend() {
pageNo++;
svg.selectAll("g.legendg").remove();
svg.select('.pageNo').remove();
svg.select('.prev').remove();
svg.select('.next').remove();
var startIndex = (pageNo - 1) * legendPerPage;
var endIndex = startIndex + legendPerPage;
var seriesSubset = [],
colorSubset = [];
for (var i = 0; i < dataSet.series.length; i++) {
if (i >= startIndex && i < endIndex) {
seriesSubset.push(dataSet.series[i]);
colorSubset.push(colors[i]);
}
}
DrawLegendSubset(seriesSubset, colorSubset, legendPerPage, pageNo, totalPages);
}
Here is an image of the final pie chart with pagination:

zoomable bar chart using d3.js

Trying to apply the following: https://bl.ocks.org/mbostock/4015254, on to my dataset. I have changed the variables to fit my dataset.
My code is the following:
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 60},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseDate = d3.timeParse("%Y-%m-%d"),
formatDate = d3.timeFormat("%Y");
var x = d3.scaleTime()
.domain([new Date(2002, 0, 1), new Date(2003, 0, 1)])
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var area = d3.area()
.curve(d3.curveStepAfter)
.y0(y(0))
.y1(function(d) { return y(d.value); });
var areaPath = g.append("path")
.attr("clip-path", "url(#clip)")
.attr("fill", "steelblue");
var yGroup = g.append("g");
var xGroup = g.append("g")
.attr("transform", "translate(0," + height + ")");
var zoom = d3.zoom()
.scaleExtent([1 / 4, 8])
.translateExtent([[-width, -Infinity], [2 * width, Infinity]])
.on("zoom", zoomed);
var zoomRect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "none")
.attr("pointer-events", "all")
.call(zoom);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
d3.json("api.php", function(d) {
d.date = parseDate(d.date);
d.value = +d.close;
return d;
}, function(error, data) {
if (error) throw error;
var xExtent = d3.extent(data, function(d) { return d.date; });
zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
y.domain([0, d3.max(data, function(d) { return d.value; })]);
yGroup.call(yAxis).select(".domain").remove();
areaPath.datum(data);
zoomRect.call(zoom.transform, d3.zoomIdentity);
});
function zoomed() {
var xz = d3.event.transform.rescaleX(x);
xGroup.call(xAxis.scale(xz));
areaPath.attr("d", area.x(function(d) { return xz(d.date); }));
}
</script>
Yet I am getting an error on my console with the following error message:
d3.v4.min.js:2 Uncaught TypeError: Cannot read property 'length' of undefined
at SVGPathElement.t (d3.v4.min.js:2)
at SVGPathElement.<anonymous> (d3.v4.min.js:2)
at ut.each (d3.v4.min.js:2)
at ut.attr (d3.v4.min.js:2)
at SVGRectElement.zoomed (research.php:123)
at k.apply (d3.v4.min.js:2)
at it (d3.v4.min.js:2)
at a.emit (d3.v4.min.js:2)
at a.zoom (d3.v4.min.js:2)
at d3.v4.min.js:2
An excerpt of my dataset looks like this:
[{"id":"1","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2006-12-29","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"23700.000000000000000000","active":"1","exchange_id":"0"},{"id":"2","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-04","time":"15:00:00.000000","close":"2416.452637000000000000","volume":"16500.000000000000000000","active":"1","exchange_id":"0"},{"id":"3","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-05","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"45400.000000000000000000","active":"1","exchange_id":"0"},{"id":"4","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-09","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"28800.000000000000000000","active":"1","exchange_id":"0"},{"id":"5","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-10","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"27800.000000000000000000","active":"1","exchange_id":"0"},{"id":"6","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-11","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"25500.000000000000000000","active":"1","exchange_id":"0"},{"id":"7","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-12","time":"15:00:00.000000","close":"2378.546875000000000000","volume":"28100.000000000000000000","active":"1","exchange_id":"0"},{"id":"8","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-15","time":"15:00:00.000000","close":"2397.500244000000000000","volume":"23400.000000000000000000","active":"1","exchange_id":"0"}]
It consists of 2831 records. I don't understand why even my x and y axes aren't printing correctly. Thanks in advance.
The bl.ocks that you refer to uses a d3.csv which (if you look at the docs) has an accessor function that is passed each row of the data and then the whole date is accessed in the callback. Here's the accessor function:
function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
return d;
}
But in case of d3.json, there's no such accessor: d3.json(url[, callback]) which means you'll have to parse each row within the callback. Here's how:
d3.json("test.json", function(data) {
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
});
var xExtent = d3.extent(data, function(d) { return d.date; });
.....
Here's a fork of your code (I'm not sure why you have the file named as "api.php" when it is a JSON file. I've used a "test.json" file.
https://bl.ocks.org/shashank2104/21358032ac5507f7a6b7d620b1a4ef69/12b6089547e3b07edeca6214834d7e7a340be6a7
If you're unable to view that or if you're looking for a code snippet, here's one:
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 60},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseDate = d3.timeParse("%Y-%m-%d"),
formatDate = d3.timeFormat("%Y");
var x = d3.scaleTime()
.domain([new Date(2006, 12, 1), new Date(2007, 1, 1)])
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var area = d3.area()
.curve(d3.curveStepAfter)
.y0(y(0))
.y1(function(d) { return y(d.value); });
var areaPath = g.append("path")
.attr("clip-path", "url(#clip)")
.attr("fill", "steelblue");
var yGroup = g.append("g");
var xGroup = g.append("g")
.attr("transform", "translate(0," + height + ")");
var zoom = d3.zoom()
.scaleExtent([1 / 4, 8])
.translateExtent([[-width, -Infinity], [2 * width, Infinity]])
.on("zoom", zoomed);
var zoomRect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "none")
.attr("pointer-events", "all")
.call(zoom);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var data = [{"id":"1","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2006-12-29","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"23700.000000000000000000","active":"1","exchange_id":"0"},{"id":"2","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-04","time":"15:00:00.000000","close":"2416.452637000000000000","volume":"16500.000000000000000000","active":"1","exchange_id":"0"},{"id":"3","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-05","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"45400.000000000000000000","active":"1","exchange_id":"0"},{"id":"4","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-09","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"28800.000000000000000000","active":"1","exchange_id":"0"},{"id":"5","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-10","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"27800.000000000000000000","active":"1","exchange_id":"0"},{"id":"6","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-11","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"25500.000000000000000000","active":"1","exchange_id":"0"},{"id":"7","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-12","time":"15:00:00.000000","close":"2378.546875000000000000","volume":"28100.000000000000000000","active":"1","exchange_id":"0"},{"id":"8","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-15","time":"15:00:00.000000","close":"2397.500244000000000000","volume":"23400.000000000000000000","active":"1","exchange_id":"0"}];
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
});
var xExtent = d3.extent(data, function(d) { return d.date; });
x.domain(xExtent);
zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
y.domain([0, d3.max(data, function(d) { return d.value; })]);
yGroup.call(yAxis).select(".domain").remove();
areaPath.datum(data);
zoomRect.call(zoom.transform, d3.zoomIdentity);
function zoomed() {
var xz = d3.event.transform.rescaleX(x);
xGroup.call(xAxis.scale(xz));
areaPath.attr("d", area.x(function(d) { return xz(d.date); }));
}
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
Hope this makes sense. Also, I'm setting the x domain to the xExtent that is computed from the data. Play around with it to see the difference.
Somehow the load function is of a different type and uses the following format.
d3.json("api.php", function(data) {
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
});
var xExtent = d3.extent(data, function(d) { return d.date; });
zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
y.domain([0, d3.max(data, function(d) { return d.value; })]);
yGroup.call(yAxis).select(".domain").remove();
areaPath.datum(data);
zoomRect.call(zoom.transform, d3.zoomIdentity);
});

KineticJs-how to update x and y position of the multiple images after resizing the stage layer

As I am new to KineticJs so, I have tried implementing the functionality using Kinectic js for drawing the multiple image on different- different x and y. Now I wanted to resize the stage layer or canvas. I have done that by using the code given below
window.onresize = function (event) {
stage.setWidth(($('#tab' + tabId).innerWidth() / 100) * 80);
var _images = layer.getChildren();
for (var i = 0; i < _images.length; i++) {
if (typeof _images[i].getId() != 'undefined') {
//alert(stage.getScale().x);
_images[i].setX(_images[i].getX() * stage.getScale().x);
layer.draw();
}
}
}
but now the problem is the are being defined and now if browser resize than stage is resized but the images on the prev x and y are fixed . I would like to keep them fixed on the position on resizing of stage layer or canvas.Here are the link of the image before resize and after resizing.beforeresize and afterResize .
Here is my entire code given below:-
$("#tabs li").each(function () {
$(this).live("click", function () {
clearInterval(_timer);
var tabname = $(this).find("a").attr('name');
tabname = $.trim(tabname.replace("#tab", ""));
var tabId = $(this).find("a").attr('href');
tabId = $.trim(tabId.replace("#", ""));
$.ajax({
url: "/Home/GetTabsDetail",
dataType: 'json',
type: 'GET',
data: { tabId: tabId },
cache: false,
success: function (data) {
var bayStatus = [];
var i = 0;
var image_array = [];
var BayExist = false;
var BayCondition;
var imgSrc;
var CanvasBacgroundImage;
var _X;
var _bayNumber;
var _Y;
var _ZoneName;
$(data).each(function (i, val) {
i = i + 1;
if (!BayExist) {
bayStatus = val.BayStatus;
CanvasBacgroundImage = val.TabImageLocation;
BayExist = true;
}
$.each(val, function (k, v) {
if (k == "BayNumber") {
BayCondition = bayStatus[v];
_bayNumber = v;
if (BayCondition == "O")
imgSrc = "../../images/Parking/OccupiedCar.gif"
else if (BayCondition == "N")
imgSrc = "../../images/Parking/OpenCar.gif";
}
if (k == "BayX")
_X = v;
if (k == "BayY")
_Y = v;
if (k == "ZoneName")
_ZoneName = v;
});
image_array.push({ img: imgSrc, xAxis: _X, yAxis: _Y, toolTip: _bayNumber, ZoneName: _ZoneName });
});
var imageUrl = CanvasBacgroundImage;
if ($('#tab' + tabId).length) {
// $('#tab' + tabId).css('background-image', 'url("../../images/Parking/' + imageUrl + '")');
var stage = new Kinetic.Stage({
container: 'tab' + tabId,
width: ($('#tab' + tabId).innerWidth() / 100) * 80, // 80% width of the window.
height: 308
});
window.onresize = function (event) {
stage.setWidth(($('#tab' + tabId).innerWidth() / 100) * 80);
}
$('#tab' + tabId).find('.kineticjs-content').css({ 'background-image': 'url("../../images/Parking/' + imageUrl + '")', 'background-repeat': ' no-repeat', 'background-size': '100% 100%' });
var layer = new Kinetic.Layer();
var planetOverlay;
function writeMessage(message, _x, _y) {
text.setX(_x + 20);
text.setY(_y + 1);
text.setText(message);
layer.draw();
}
var text = new Kinetic.Text({
fontFamily: 'Arial',
fontSize: 14,
text: '',
fill: '#000',
width: 200,
height: 60,
align: 'center'
});
var opentooltip = new Opentip(
"div#tab" + tabId, //target element
"dummy", // will be replaced
"", // title
{
showOn: null // I'll manually manage the showOn effect
});
Opentip.styles.win = {
borderColor: "black",
shadow: false,
background: "#EAEAEA"
};
Opentip.defaultStyle = "win";
// _timer = setInterval(function () {
for (i = 0; i < image_array.length; i++) {
img = new Image();
img.src = image_array[i].img;
planetOverlay = new Kinetic.Image({
x: image_array[i].xAxis,
y: image_array[i].yAxis,
image: img,
height: 18,
width: 18,
id: image_array[i].toolTip,
name: image_array[i].ZoneName
});
planetOverlay.on('mouseover', function () {
opentooltip.setContent("<span style='color:#87898C;'><b>Bay:</b></span> <span style='color:#25A0D3;'>" + this.getId() + "</span><br> <span style='color:#87898C;'><b>Zone:</b></span><span style='color:#25A0D3;'>" + this.getName() + "</span>");
//writeMessage("Bay: " + this.getId() + " , Zone: " + this.getName(), this.getX(), this.getY());//other way of showing tooltip
opentooltip.show();
$("#opentip-1").offset({ left: this.getX(), top: this.getY() });
});
planetOverlay.on('mouseout', function () {
opentooltip.hide();
// writeMessage('');
});
planetOverlay.createImageHitRegion(function () {
layer.draw();
});
layer.add(planetOverlay);
layer.add(text);
stage.add(layer);
}
// clearInterval(_timer);
//$("#tab3 .kineticjs-content").find("canvas").css('background-image', 'url("' + imageUrl + '")');
// },
// 500)
}
}
,
error: function (result) {
alert('error');
}
});
});
});
I want to keep the icons on the position where they were before resizing. I have tried but could not get the right solution to get this done.
How can How can I update x,y position for the images . Any suggestions would be appreciated.
Thanks is advance.
In window.resize, you're changing the stage width by a scaling factor.
Save that scaling factor.
Then multiply the 'x' coordinate of your images by that scaling factor.
You can reset the 'x' position of your image like this:
yourImage.setX( yourImage.getX() * scalingFactor );
layer.draw();
In the above mentioned code for window.onresize. The code has been modified which as follow:-
window.onresize = function (event) {
_orignalWidth = stage.getWidth();
var _orignalHeight = stage.getHeight();
// alert(_orignalWidth);
// alert($('#tab' + tabId).outerHeight());
stage.setWidth(($('#tab' + tabId).innerWidth() / 100) * 80);
//stage.setHeight(($('#tab' + tabId).outerHeight() / 100) * 80);
_resizedWidth = stage.getWidth();
_resizedHeight = stage.getHeight();
// alert(_resizedWidth);
_scaleFactorX = _resizedWidth / _orignalWidth;
var _scaleFactorY = _resizedHeight / _orignalHeight;
//alert(_scaleFactor);
var _images = layer.getChildren();
for (var i = 0; i < _images.length; i++) {
if (typeof _images[i].getId() != 'undefined') {
//alert(stage.getScale().x);
_images[i].setX(_images[i].getX() * _scaleFactorX);
//_images[i].setY(_images[i].getY() * _scaleFactorY);
layer.draw();
}
}
}

Having issues with moving div

I am trying to create a 300px by 300 px div box that moves with mouse. The only thing is when the visitor click on div it disappear. Any help would be greatly appreciated.
if((document.getElementById) && window.addEventListener ||
window.attachEvent){ (function(){ var hairCol = "#ff0000";
var d = document; var my = -10; var mx = -10; var r; var
vert = "";
var idx = document.getElementsByTagName('div').length;
var thehairs = "<div id='theiframe' scrolling='no'
style='position:absolute;width:53px;height:23px;overflow:hidden;border:0;opacity:"
+ opacity +";filter:alpha(opacity=" + opacity * 100+ ");'>dsdsds"; document.write(thehairs); var like =
document.getElementById("theiframe");
document.getElementsByTagName('body')[0].appendChild(like);
var pix = "px"; var domWw = (typeof window.innerWidth ==
"number"); var domSy = (typeof window.pageYOffset == "number");
if (domWw) r = window; else{ if (d.documentElement &&
typeof d.documentElement.clientWidth == "number" &&
d.documentElement.clientWidth != 0)
r = d.documentElement; else{
if (d.body && typeof d.body.clientWidth == "number")
r = d.body; } }
if(time != 0){ setTimeout(function(){
document.getElementsByTagName('body')[0].removeChild(like);
if (window.addEventListener){
document.removeEventListener("mousemove",mouse,false);
}
else if (window.attachEvent){
document.detachEvent("onmousemove",mouse);
}
}, time); }
function scrl(yx){ var y,x; if (domSy){
y = r.pageYOffset;
x = r.pageXOffset; } else{
y = r.scrollTop;
x = r.scrollLeft; } return (yx == 0) ? y:x; }
function mouse(e){ var msy = (domSy)?window.pageYOffset:0; if
(!e)
e = window.event; if (typeof e.pageY == 'number'){
my = e.pageY - 5 - msy;
mx = e.pageX - 4; } else{
my = e.clientY - 6 - msy;
mx = e.clientX - 6; } vert.top = my + scrl(0) + pix; vert.left = mx + pix; }
function ani(){ vert.top = my + scrl(0) + pix; setTimeout(ani,
300); }
function init(){ vert =
document.getElementById("theiframe").style; ani(); } if
(window.addEventListener){
window.addEventListener("load",init,false);
document.addEventListener("mousemove",mouse,false); } else if
(window.attachEvent){ window.attachEvent("onload",init);
document.attachEvent("onmousemove",mouse); }
})();
}//End.