js code and svg path is not working in browser - html

This is my fiddle which is perfectly running http://jsfiddle.net/demo_Ashif/tmQ2H/
//
var rsrGroups = [Dist1,Dist2];
for (var i = 0, len = rsrGroups.length; i <= len; i++) {
var el = rsrGroups[i];
el.mouseover(function() {
this.toFront();
this.attr({
cursor: 'pointer',
fill: '#990000',
stroke: '#fff',
'stroke-width': '2'
});
this.animate({
scale: '1.2'
}, 200);
});
el.mouseout(function() {
this.animate({
scale: '1.05'
}, 200);
this.attr({
fill: '#003366'
});
});
el.click(function() {
this.animate({
fill: 'green'
}, 200);
});
}
//
But same code is not running in my index.html page. same in Chrome and Firefox. I am not understanding the fault. please someone solve it.

You're using a very old version of RaphaelJS -- 1.5.2 -- in your fiddle. You should be using 2.1.0, which you probably are in the browser, hence the error.
The problem is that Dist1 and Dist2 are sets, not elements, which don't directly take events. But there's no need to do this since you only have one item per set. Just assign the variables directly to the path objects:
var Dist1 = rsr.path("m 184.17891,318.14557...")
var Dist2 = rsr.path("m 352.66606,280.9633...")
A few other errors:
The arguments to Raphael are reversed: Should be var rsr = Raphael('rsr', 0, 0, 612, 792), with x and y and then width and length
Your for loop is starting at index 2 for a zero-indexed two-item array. In any event, it would be better to use rsrGroups.forEach(function(el) {...
In Raphael 2.1.0, you can't directly animate the scale. Simple fix:
this.animate({
transform: "s1.2"
}, 200);
Here's a working fiddle, but please take a moment to see what I did -- I commented out your code where it was breaking.
(I wrote the O'Reilly book on RaphaelJS if you plan to keep using it, and assure you that any royalties at this point are negligible, so I'm not out to make any money here!)

Related

HTML 5 wait for drawImage to finish in a Vue SPA

I have been trying to debug something for a week and I now suspect the problem is that the drawImage function does not have time to finish. I have a for loop that composes a canvas element by stitching together two different canvas elements and then add that composedCanvas as a frame to a GIF.js object. The problem I keep running into is that the bottom stitched canvas does not appear or partially appears (the picture below started to draw but did not finish) in my output GIF file. My question is how do I ensure synchronous execution of drawImage in the context of a Vue SPA method. I have experimented with Promise, but I have not gotten it to work. Can anyone explain and help me with this, please?
EDIT : I have tried wrapping my drawImage in a promise and await but it raised type errors.
I managed to get it working by properly wrapping the drawImage step in a separate method and inside a promise the proper way. See the code below for an example of two methods that were the culprits but are now fixed.
async composeCanvas( gif , timeStep , visibleLayers , delayInput) {
const mapCnv = this.getMapCanvas();
await this.updateInfoCanvas( timeStep )
const numberVisibleLayers = visibleLayers.length;
const composedCnv = await this.stitchCanvases( mapCnv , numberVisibleLayers );
gif.addFrame(composedCnv, {copy:false, delay: delayInput});
},
async stitchCanvases( mapCanvas , numberVisibleLayers ) {
return new Promise(( resolve ) => {
var composedCnv = document.createElement('canvas');
var ctx = composedCnv.getContext('2d');
var ctx_w = mapCanvas.width;
var ctx_h = mapCanvas.height + ((numberVisibleLayers - 1) * 30) + 40;
composedCnv.width = ctx_w;
composedCnv.height = ctx_h;
[
{
cnv: mapCanvas,
y: 0
},
{
cnv: this.infoCanvas,
y: mapCanvas.height
}
].forEach( ( n ) => {
ctx.beginPath();
ctx.drawImage(n.cnv, 0, n.y, ctx_w, n.cnv.height);
});
resolve(composedCnv)
})
}

Dashed Line SVG Animation

I'm newer to working with SVG animations so I apologize in advanced if there is a straightforward answer to this. With a combination of utilizing Scrollmajic.js and GASP I was able to get the SVG to animate when the user scrolls to a specific position. The issue i'm running into is that the animation converts it from a dashed line to a solid stroke. I've done some research and found that using a mask would be the best way to accomplish this though I'm a bit lost on how to approach this method.
https://codepen.io/ncanarelli/pen/wvaeLNa
js:
$(document).ready(function() {
function pathPrepare ($el) {
var lineLength = $el[0].getTotalLength();
$el.css("stroke-dasharray", lineLength);
$el.css("stroke-dashoffset", lineLength);
}
// init controller
var controller = new ScrollMagic.Controller();
// loop through all elements
$(".svg-animation").each(function() {
var $thePath = $(this).find("path#thePath");
// prepare SVG
pathPrepare($thePath);
// build tween
var tween = new TimelineMax()
.add(TweenMax.to($thePath, 1, {
strokeDashoffset: 0,
ease:Linear.easeNone
}))
// build scene
var scene = new ScrollMagic.Scene({
triggerElement: $(this)[0],
duration: 200,
tweenChanges: true,
reverse: true
})
.setTween(tween)
.addTo(controller)
.addIndicators();
});
});
UPDATE: Updated .js to reflect an .each loop + other modifications. Still having issues with implementing a mask.

Reading nodes data from local variable in d3

How to create a d3 graph from local variable jsonNodes (JSON data) rather than reading from a file, the example I am using Andrew-Reid’s online example on bl.ocks.
Following is the code I am trying, the button 'Go' is calling the refresh function to refresh the graph by using the jsonNodes variable. ( I am actually calling a WebSocket service to get the jsonNodes populated but as of now i have just hardcoded it in the function).
when I click the 'Go' button, it does nothing.
<canvas width="960" height="500"></canvas>
<body>
<button id="refresh" onclick="refresh();">Go</button>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var canvas = document.querySelector("canvas"),
context = canvas.getContext("2d"),
width = canvas.width,
height = canvas.height;
var jsonNodes;
function refresh(){
jsonNodes = {
"nodes": [
{"id": "Myriel", "group": 1},
{"id": "Napoleon", "group": 1},
{"id": "Mlle.Baptistine", "group": 1} ],
"links": [
{"source": "Napoleon", "target": "Myriel", "value": 1} ]
} ;
simulation
.nodes(jsonNodes.nodes)
.force("link")
.links(jsonNodes.links);
};
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("../data/miserables.json", function(error, graph) {
if (error) throw error;
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
d3.select(canvas)
.call(d3.drag()
.container(canvas)
.subject(dragsubject));
function ticked() {
var margin = 20;
graph.nodes.forEach(function(d) {
d.x = Math.max(margin, Math.min(width - margin, d.x))
d.y = Math.max(margin, Math.min(height - margin, d.y))
})
context.clearRect(0, 0, width, height);
context.beginPath();
graph.links.forEach(drawLink);
context.strokeStyle = "#aaa";
context.stroke();
context.beginPath();
graph.nodes.forEach(drawNode);
context.fill();
context.strokeStyle = "#fff";
context.stroke();
}
function dragsubject() {
return simulation.find(d3.event.x, d3.event.y);
}
});
function drawLink(d) {
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
}
function drawNode(d) {
context.moveTo(d.x + 3, d.y);
context.arc(d.x, d.y, 3, 0, 2 * Math.PI);
}
</script>
</body>
Based on your updated post with the sample code, there are few things which may be causing failure to achieve desired results.
Firstly, I can see that the jsonNodes is an object in the desired format already.
Secondly, since the data is already in the desired format in a variable, you do not need to parse it in using d3.json("../data/miserables.json", function(error, graph) {
if (error) throw error;.
Also in the example by Andrew Reid, he uses the object graph, which in your case would jsonNodes. So where ever, you see graph.nodes or graph.links replace that with jsonNodes.nodes and jsonNodes.links. A simpler alternative to changing all individual locations of this is to transfer the data from jsonNodes to a variable called graph, and use the graph variable as in Andrew Reid's example.
Lastly, the order of you declaring simulation is wrong. You are calling
simulation
.nodes(jsonNodes.nodes)
.force("link")
.links(jsonNodes.links);
};
before
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
This is opposite.
Here is a plunker with everything fixed for you:
https://plnkr.co/edit/LcsGes?p=preview
Some other examples: http://jsfiddle.net/DavidHoksza/0d7en4po/ which directly uses data in a variable. However, keep in mind that this example uses d3 v3 and the syntax for the force simulation has changed. There are many more examples which use variable directly in force simulations: http://blockbuilder.org/kenpenn/268878b410ddb1201277be3f0305d566
You can search for more examples using http://blockbuilder.org/search
I hope this helps. If not please create plunker / block for me to check what you have going exactly. And populate more data into the jsonNodes variable.

Show bar with zero value in ChartJs v2

I'm wondering is there any way to show in ChartJs (in bar chart) bars with zero value?
I mean something like this: https://jsfiddle.net/vrg5cnk5/16/, so the second bar would be just blue border on level zero.
I used already this code:
ticks: {
beginAtZero: true,
min: 0,
suggestedMin: 0
}
but I'm not surprised it didn't work.
Thanks in advance
Simply specify minBarLength in the dataset, with the minimum length in pixels the bars should have. See documentation.
Working Example:
var $chartCanvas = $('myCanvas');
var barChart = new Chart(myCanvas, {
type: 'bar',
data: {
labels: ['Accepted Answer', 'Top rated answer', 'This Answer'],
datasets:[{
data: [0, 3, 10],
minBarLength: 7, // This is the important line!
}]
},
options: {
title: {
display: true,
text: 'helpfulness of answers to this questions'
},
legend: {
display: false
}
}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="myCanvas"></canvas>
After dig into the plugin system, if you using Chart.js >=2.5, you can write a plugin to achieve it. Here is an example to draw a line when data is zero.
Here is my code:
const zeroCompensation = {
renderZeroCompensation: function (chartInstance, d) {
// get postion info from _view
const view = d._view
const context = chartInstance.chart.ctx
// the view.x is the centeral point of the bar, so we need minus half width of the bar.
const startX = view.x - view.width / 2
// common canvas API, Check it out on MDN
context.beginPath();
// set line color, you can do more custom settings here.
context.strokeStyle = '#aaaaaa';
context.moveTo(startX, view.y);
// draw the line!
context.lineTo(startX + view.width, view.y);
// bam! you will see the lines.
context.stroke();
},
afterDatasetsDraw: function (chart, easing) {
// get data meta, we need the location info in _view property.
const meta = chart.getDatasetMeta(0)
// also you need get datasets to find which item is 0.
const dataSet = chart.config.data.datasets[0].data
meta.data.forEach((d, index) => {
// for the item which value is 0, reander a line.
if(dataSet[index] === 0) {
this.renderZeroCompensation(chart, d)
}
})
}
};
and here is how to add the plugin to Chart.js
var chart1 = new Chart(ctx, {
plugins: [zeroCompensation]
});
The offcial document is not clear about their plugin API, you may console.log to find what you want.
There is no way to configure chart.js to do this, but you could use a hack instead. Just set your value for the 0 bar to a really small number like 0.1.
data: [2, 0.1, 3, 1]
Here is an example forked from yours.
If you are using tooltips, then you would have to also add some logic so that the tooltip for that bar still displays a value of 0. You can do this using the label callback.
label: function(tooltipItem, data) {
var value = data.datasets[0].data[tooltipItem.index];
var label = data.labels[tooltipItem.index];
if (value === 0.1) {
value = 0;
}
return label + ': ' + value + ' %';
}
Here is an example putting it all together.
If you struggle with this, here's what I came up with. It is similar idea to Li Jinyao, but in addition, you would get click and hover events (tooltip) working for whole bar.
I value is close to 0 but negative, the bar will show on negative side of x axis - you can easily get rid of it if that's not what you want to do.
const zeroCompensation = {
id: 'zeroCompensation',
beforeDatasetsDraw: function(chart) {
const meta = chart.getDatasetMeta(0)
forEach(meta.data, d => {
const barHeight = d._view.base - d._view.y;
if(Math.abs(barHeight) < minBarHeight /* I used value 5 */) {
d._view.y = d._view.base - minBarHeight * (Math.sign(barHeight) || 1);
}
});
}};
and add it to plugins:
plugins: [zeroCompensation]
Keep in mind that this will work for values close to 0, not only 0. If you want it only for zeroes, you can change contents of if condition to:
chart.config.data.datasets[0].data[index] === 0
This is what Li Jinyao used in his answer.
Hope that helps.
Edit: I wanted to highlight that this solution works regardless of values spread. Answer marked as solution will not work as intended if there are some high values in data set - 0.1 will render same as 0 in that case.
Here is the simplest way to do this in V3 chart js
Chart.defaults.datasets.bar.minBarLength = 5;
2019 Update
This can be done easily as below.
var myChart = new Chart(ctx, {
...
options: {
...
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
);
You can find this in Chart.js documentation https://www.chartjs.org/docs/latest/
I just stumbled over this questions because I had a similar problem: The type of my Chart.js chart was 'horizontalBar' and for some datasets (where no zero values was present across the dataset) the horizontal bar did not start with 0 rather with the lowest value from the dataset.
I tried to figure out a solution and came up with following entry in the options object while creating the charts:
ticks: {
beginAtZero:true,
mirror:false,
suggestedMin: 0,
suggestedMax: 100
}
However that did not work as expected although all posts said it works that way.
After further investigation and reading of the Chart.js documentation I found the solution. The reason the further step did not work was following I found in the documentation:
However, any options specified on the x axis in a bar chart, are applied to the y axis in a horizontal bar chart.
So I just changed my options object to hold the proper configuration for the xAxes and it worked.
For those who are interested here is the the whole code I used for creating the horizontal bar chart with y-axis starting always at zero:
this.chart = new Chart(
ctx,
{
type: 'horizontalBar',
data: this.data.chartdata,
options: {
scales: {
xAxes: [{
stacked: false,
ticks: {
beginAtZero:true,
mirror:false,
suggestedMin: 0,
suggestedMax: 100
}
}],
yAxes: [{
stacked: true
}]
},
scaleBeginAtZero : true,
// important here to use () =>
// to keep the scope of this
onClick: (e) => {
var actChart : Chart = this.charts[trialId];
var element =
actChart.getElementAtEvent(e);
}
}
}
);

change the body background every 10 seconds

I'm looking for a really simple and lightweight code that change the body background of the website (it can go with the css also) every 10 seconds, it's supposed to be something easy with jquery and css, right?
how can i do that?
You can use JavaScript's setTimeout() function. Here is an example of how to use it.
For setTimeout(), you need 2 parameters, one being your function, and the other an integer, which is how long your timeout will last.
So for your instance, you can do:
$(function() {
var newBg = ['img1.png', 'img2.jpg', 'img3.png'];
var path="img/";
var i = 0;
var changeBg = setTimeout(function() {
$('body').css({backgroundImage : 'url(' path + newBg[i] + ')'});
if(i == newBg.length)
i=0;
else
i++;
}, 10000);
});
This creates an array of your images, specified with a path. Thus I create a variable which checks the length of your arrayList, and whether or not you need to reset the variable i.
So i hope that helps :)
custom-background.js is a lightweight jQuery plugin to allow users change the website’s default background and saves the background selected to local storage. This plugin can easily be added to any website or web app without any compromise on website's performance.
https://github.com/CybrSys/custom-background.js
You can easily do this with jQuery.
A simple javascript function to toggle between two colors every X seconds
function toggle_color(color1,color2,cycle_time,wait_time) {
setInterval(function first_color() {
$("body").animate({
backgroundColor: color1
}, 1000, function () {
setTimeout(change_color, wait_time);
});
}, cycle_time);
function change_color() {
$("body").animate({
backgroundColor: color2
}, 1000, function () {
setTimeout(function () {}, wait_time);
});
}
}
You can check Changing background color every X seconds in Javascript for working demo and explanation.