Related
I have made a custom plugin to always show the tooltips since that was what I needed.
The only thing I would like to have now is a on-hover action.
const alwaysShowTooltipPlugin = {
id: 'alwaysShowTooltip',
onHover: function (evt, item, legend) {
console.log('hovered')
},
afterDraw(chart, args, options) {
const { ctx } = chart
ctx.save()
chart.data.datasets.forEach((dataset, i) => {
chart.getDatasetMeta(i).data.forEach((datapoint, index) => {
const { x, y } = datapoint.tooltipPosition();
const text = dataset.label
const textWidth = ctx.measureText(text).width
ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'
ctx.fillRect(x - ((textWidth + 20) / 2), y - 40, textWidth + 20, 30)
// Triangle
ctx.beginPath()
ctx.moveTo(x, y )
ctx.lineTo(x - 5, y - 10)
ctx.lineTo(x + 5, y - 10)
ctx.fill()
ctx.restore()
// Text
ctx.font = '14px Arial'
ctx.fillStyle = 'white'
ctx.fillText(text, x - (textWidth / 2), y - 19)
ctx.restore()
})
})
},
}
How can I add a eventListener for a onClick so that I can use the data that was clicked on? The onHover I have currently does not work. It doesn't do anything at all.
I am using Nuxt by the way and Vue-ChartJS latest version.
I am new to Chart.Js and facing issues on click event on bar chart. I would like to get the repective x-axis lable on click function . But I am receiving error "barchart.getElementsAtEvent is not a function at HTMLCanvasElement.Graphinit.document.getElementById.onclick". I really do not understand what exactly is wrong in my code.
I am calling the below function to generate bar chart The min, item are data array for x-axis & yaxis.
cdn : "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"
var barchart;
function Graphinit(min, item, labelstring, stepcount, scaleStepWidth, reporttype, min1, item1) {
$('#myChart').remove(); // this is my <canvas> element
$('#showgraph').append(' <canvas id="myChart" width="1200" height="400"></canvas>');
inputdata = min;
xaxislabel = item;
Chart.types.Bar.extend({
name: "BarAlt",
draw: function () {
Chart.types.Bar.prototype.draw.apply(this, arguments);
var ctx = this.chart.ctx;
ctx.save();
// text alignment and color
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
ctx.fillStyle = this.options.scaleFontColor;
// position
var x = this.scale.xScalePaddingLeft * 0.4;
var y = this.chart.height / 2;
// change origin
ctx.translate(x, y)
// rotate text
ctx.rotate(-90 * Math.PI / 180);
ctx.fillText(this.datasets[0].label, 0, 0);
ctx.restore();
}
});
var bridgeDataPoints = [];
if ((reporttype === "Lines picked per user") || (reporttype === "Picks per item") || (reporttype === "Lines Picked by item class") || (reporttype == "Lines picked by customer") || (reporttype == "Total Lines Picked") || reporttype == "Skipped Lines") {
for (var j = 0; j < min.length; j++) {
var dataPoint = new DataPoint1(item[j], min[j]);
bridgeDataPoints.push(dataPoint);
}
}
var data = {
labels: bridgeDataPoints,
datasets: [{
label: labelstring,
fillColor: "rgba(255, 255, 255, 1)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: inputdata
}]
};
var barchartoptions = {
//Boolean - If we show the scale above the chart data
scaleOverlay: false,
//Boolean - If we want to override with a hard coded scale
scaleOverride: true,
//** Required if scaleOverride is true **
//Number - The number of steps in a hard coded scale
scaleSteps: stepcount,
//Number - The value jump in the hard coded scale
scaleStepWidth: scaleStepWidth,
//Number - The scale starting value
scaleStartValue: 0,
scaleEndValue: stepcount,
//String - Colour of the scale line
scaleLineColor: "rgba(0,0,0,.1)",
//Number - Pixel width of the scale line
scaleLineWidth: 1,
//Boolean - Whether to show labels on the scale
scaleShowLabels: true,
//Interpolated JS string - can access value
scaleLabel: " <%=value%>",
//String - Scale label font declaration for the scale label
scaleFontFamily: "'Arial'",
//Number - Scale label font size in pixels
scaleFontSize: 12,
//String - Scale label font weight style
scaleFontStyle: "normal",
//String - Scale label font colour
scaleFontColor: "#666",
///Boolean - Whether grid lines are shown across the chart
scaleShowGridLines: true,
//String - Colour of the grid lines
scaleGridLineColor: "rgba(0,0,0,.05)",
//Number - Width of the grid lines
scaleGridLineWidth: 1,
//Boolean - If there is a stroke on each bar
barShowStroke: true,
//Number - Pixel width of the bar stroke
barStrokeWidth: 2,
//Number - Spacing between each of the X value sets
barValueSpacing: 10,
//Number - Spacing between data sets within X values
barDatasetSpacing: 3,
//Boolean - Whether to animate the chart
animation: true,
//Number - Number of animation steps
animationSteps: 60,
//String - Animation easing effect
animationEasing: "easeOutQuart",
//Function - Fires when the animation is complete
onAnimationComplete: function (animation) {
//alert('onAnimationComplete')
},
tooltipTemplate: "<%if (label){%><%=label.tooltip%><%}%>"
};
barchartoptions.datasetFill = false;
var ctx = $("#myChart").get(0).getContext('2d');
ctx.canvas.height = 400; // setting height of canvas
ctx.canvas.width = 1200; // setting width of canvas
//var config = Chart.defaults.bar;
barchart = new Chart(ctx).BarAlt(data, barchartoptions);
var length = inputdata.length; //min1.length;
var backgroundcolorset = [];
for (var i = 0; i < length; i++) {
var rgb = [];
for (var j = 0; j < 3; j++) {
rgb.push(Math.floor(Math.random() * 255));
}
backgroundcolorset.push('rgb(' + rgb.join(',') + ')');
}
for (var j = 0; j < length; j++) {
barchart.datasets[0].bars[j].fillColor = backgroundcolorset[j]; //bar 1
}
barchart.update();
console.log()
//console.log(barchart.getElementAtEvent(e));
document.getElementById("myChart").onclick = function (evt) {
// var activePoints =
console.log(barchart);
var activePoints = barchart.getElementsAtEvent(evt);
};
} // end Graphinit
Famous.Engine = famous.core.Engine;
Famous.Surface = famous.core.Surface;
Famous.RenderNode = famous.core.RenderNode;
Famous.ContainerSurface = famous.surfaces.ContainerSurface;
Famous.ScrollView = famous.views.Scrollview;
Famous.SequentialLayout = famous.views.SequentialLayout;
Famous.Transform = famous.core.Transform;
Famous.Transitionable = famous.transitions.Transitionable;
Famous.SnapTransition = famous.transitions.SnapTransition;
Famous.TransitionableTransform = famous.transitions.TransitionableTransform;
Famous.Modifier = famous.core.Modifier;
Famous.StateModifier = famous.modifiers.StateModifier;
Famous.Easing = famous.transitions.Easing;
Famous.EventHandler = famous.core.EventHandler;
var projectsList = document.getElementById('projects-list');
var mainContext = Famous.Engine.createContext(projectsList);
var sequentialLayout = new Famous.SequentialLayout({
direction: 0
});
Famous.Transitionable.registerMethod('snap', Famous.SnapTransition);
var snap = { method: 'snap', period: 600, dampingRatio: 0.6 }
var surfaces = [];
for (var i = 0; i < 20; i++) {
var surface = new Famous.Surface({
content: "Surface: " + (i + 1),
size: [300, $(window).height()],
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
textAlign: "center"
}
});
surface.open = false;
surface.state = new Famous.Modifier({
size: [undefined, undefined]
});
surface.trans = new Famous.Transitionable(300);
surface.state.sizeFrom(function(){
return [this.trans.get(), undefined];
}.bind(surface));
surface.node = new Famous.RenderNode();
surface.node.add(surface.state).add(surface);
surface.pipe(sequentialLayout);
surface.on('click',function(e){
if (this.open) {
this.trans.halt();
this.trans.set(300, snap);
} else {
this.trans.halt();
this.trans.set($(window).width(), snap);
}
this.open = !this.open;
}.bind(surface));
surfaces.push(surface.node);
sequentialLayout.sequenceFrom(surfaces);
}
mainContext.add(sequentialLayout);
The surfaces translate their X positions fine, but do not change their width from 300px. I would also like the surface that's clicked on to change its width to fill the window's width if its in a closed state. I would like the surface to go back to its initial width of 300px if it is clicked in an open state. How do I accomplish this so the width animates rather than jumps to the specified sizes?
Remove the size of the Surface
var surface = new Famous.Surface({
content: "Surface: " + (i + 1),
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
textAlign: "center"
}
});
The key is to not specify a static width or height when creating the surfaces:
var surface = new Famous.Surface({
content: "Surface: " + (i + 1),
size: [undefined, undefined], /* removed the original [300, $(window).height()] */
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
textAlign: "center"
}
});
I used raphael.js to draw some element's, and now I want to connect them,
how to connect raphael elements with jsplumb ?
For example, in the JSFiddle below : I'm trying to create connections between 2 circles, but I can't, because the circles have no id, my code .
i can't use this :
jsPlumb.connect({source:"id1", target:"id2"})
I finally found a solution to connect raphael elements with jsplumb, I want to thank Mr: # Simon Porritt (jsPlumb creator) for his help: the solution
jsPlumb.ready(function(){
jsPlumb.Defaults.Container = "drawing_board";
// Create a 480 x 640 canvas.
var paper = Raphael('drawing_board');
// of 90 pixels.
var circle1 = paper.circle(140, 110, 90).attr({ fill: '#3D6AA2', stroke: '#000000', 'stroke-width': 8 });
var circle2 = paper.circle(400, 180, 90).attr({ fill: '#3D6AA2', stroke: '#000000', 'stroke-width': 8 });
var rect = paper.rect(50, 280, 90, 70).attr({ fill: '#3D6AA2', stroke: '#000000', 'stroke-width': 8 });
var ellipse = paper.ellipse(300, 420, 90, 70).attr({ fill: '#3D6AA2', stroke: '#000000', 'stroke-width': 8 });
var offsetCalculators = {
"CIRCLE":function(el, parentOffset) {
var cx = parseInt(el.attr("cx"), 10),
cy = parseInt(el.attr("cy"), 10),
r = parseInt(el.attr("r"), 10);
return {
left: parentOffset.left + (cx - r),
top:parentOffset.top + (cy - r)
};
},
"ELLIPSE":function(el, parentOffset) {
var cx = parseInt(el.attr("cx"), 10),
cy = parseInt(el.attr("cy"), 10),
rx = parseInt(el.attr("rx"), 10),
ry = parseInt(el.attr("ry"), 10);
return {
left: parentOffset.left + (cx - rx),
top:parentOffset.top + (cy - ry)
};
},
"RECT":function(el, parentOffset) {
var x = parseInt(el.attr("x"), 10),
y = parseInt(el.attr("y"), 10);
return {
left: parentOffset.left + x,
top:parentOffset.top + y
};
}
};
var sizeCalculators = {
"CIRCLE":function(el) {
var r = parseInt(el.attr("r"), 10);
return [ r * 2, r * 2 ];
},
"ELLIPSE":function(el) {
var rx = parseInt(el.attr("rx"), 10),
ry = parseInt(el.attr("ry"), 10);
return [ rx * 2, ry * 2 ];
},
"RECT":function(el) {
var w = parseInt(el.attr("width"), 10),
h = parseInt(el.attr("height"), 10);
return [ w, h ];
}
};
jsPlumb.CurrentLibrary.getOffset = function(el) {
el = $(el);
var del = el[0], tn = del.tagName.toUpperCase();
if (offsetCalculators[tn]) {
var so = el.parent().offset();
return offsetCalculators[tn](el, so);
}
else
return el.offset();
};
jsPlumb.CurrentLibrary.getSize = function(el) {
el = $(el);
var del = el[0], tn = del.tagName.toUpperCase();
if (sizeCalculators[tn]) {
return sizeCalculators[tn](el);
}
else
return [ el.outerWidth(), el.outerHeight() ];
};
jsPlumb.connect({source:circle1.node, target:circle2.node, anchor:"Center", connector:"Straight"});
jsPlumb.connect({source:circle1.node, target:rect.node, anchors:["Center", "Top"], connector:"Straight"});
jsPlumb.connect({source:circle2.node, target:ellipse.node, anchor:"Center"});
//raphael draggable
var start = function () {
this.ox = this.attr("cx");
this.oy = this.attr("cy");
},
move = function (dx, dy) {
this.attr({cx: this.ox + dx, cy: this.oy + dy});
jsPlumb.repaint(this.node);
},
up = function () { };
paper.set(circle1, circle2).drag(move, start, up); });
What would be the best way to draw a rectangle or circle for that matter using your mouse ?
I just started looking at raphael and it seems I should use, drag? Or mousedown and mouseup ?
No sure.
I would use mousedown and mouseup events on your canvas/paper. On mouse down you should store the x and y positions of the mousedown, then on mouseup you should use the current mouse positions to calculate width and height.
Here is an example, bear in mind thought that I am using JQuery to calculate the mouse positions (if this is not available for you, then you should find another way to get the mouse offsets)
//global variables
var mouseDownX = 0;
var mouseDownY = 0;
//register events on document load
paper.mousedown(OnMouseDown);
paper.mouseup(OnMouseUp);
function OnMouseDown(e){
var offset = $("#Canvas").offset();//This is JQuery function
mouseDownX = e.pageX - offset.left;
mouseDownY = e.pageY - offset.top;
}
function OnMouseUp(e){
var offset = $("#Canvas").offset();//This is JQuery function
var upX = e.pageX - offset.left;
var upY = e.pageY - offset.top;
var width = upX - mouseDownX;
var height = upY - mouseDownY;
DrawRectangle(mouseDownX, mouseDownY, width, height);
}
function DrawRectangle(x, y, w, h){
var element = paper.rect(x, y, w, h);
element.attr({
fill: "#FFF",
stroke: "#F00"
});
}
Hope that helps
Here's an updated version of fiddle (mentioned in the other article) that works with Raphael 2+ (no paper events).
This fiddle shows how a rectangle is dynamically drawn while you drag the mouse.
var mouseDownX = 0;
var mouseDownY = 0;
var elemClicked;
var rect;
$(document).ready(function() {
var paper = Raphael("d1", 300, 200);
// start, move, and up are the drag functions
start = function() {
// storing original coordinates
this.ox = this.attr("x");
this.oy = this.attr("y");
this.attr({
opacity: 1
});
if (this.attr("y") < 60 && this.attr("x") < 60) this.attr({
fill: "#000"
});
}, move = function(dx, dy) {
// move will be called with dx and dy
if (this.attr("y") > 200 || this.attr("x") > 300) this.attr({
x: this.ox + dx,
y: this.oy + dy
});
else {
nowX = Math.min(300, this.ox + dx);
nowY = Math.min(200, this.oy + dy);
nowX = Math.max(0, nowX);
nowY = Math.max(0, nowY);
this.attr({
x: nowX,
y: nowY
});
if (this.attr("fill") != "#000") this.attr({
fill: "#000"
});
}
}, up = function() {
// restoring state
this.attr({
opacity: .5
});
if (this.attr("y") < 60 && this.attr("x") < 60) this.attr({
fill: "#AEAEAE"
});
};
function DrawRectangle(x, y, w, h) {
var element = paper.rect(x, y, w, h);
element.attr({
fill: "gray",
opacity: .5,
stroke: "#F00"
});
$(element.node).attr('id', 'rct' + x + y);
console.log(element.attr('x'));
element.drag(move, start, up);
element.click(function(e) {
elemClicked = $(element.node).attr('id');
});
return element;
}
$("#bind").click(function(e) {
$('#d1').unbind('mousedown');
$('#d1').unbind('mousemove');
$('#d1').unbind('mouseup');
$("#d1").mousedown(function(e) {
// Prevent text edit cursor while dragging in webkit browsers
e.originalEvent.preventDefault();
var offset = $("#d1").offset();
mouseDownX = e.pageX - offset.left;
mouseDownY = e.pageY - offset.top;
rect = DrawRectangle(mouseDownX, mouseDownY, 0, 0);
$("#d1").mousemove(function(e) {
var offset = $("#d1").offset();
var upX = e.pageX - offset.left;
var upY = e.pageY - offset.top;
var width = upX - mouseDownX;
var height = upY - mouseDownY;
rect.attr( { "width": width > 0 ? width : 0,
"height": height > 0 ? height : 0 } );
});
});
$("#d1").mouseup(function(e) {
$('#d1').unbind('mousemove');
var BBox = rect.getBBox();
if ( BBox.width == 0 && BBox.height == 0 ) rect.remove();
});
});
$("#unbind").click(function(e) {
$('#d1').unbind('mouseup');
$('#d1').unbind('mousemove');
$('#d1').unbind('mousedown');
});
$("#clr").click(function(e) {
$('#d1').find('rect').each(function(i, obj) {
$(this).remove();
});
});
$("#del").click(function(e) {
$('#' + elemClicked).remove();
});
});
I have tried to modify the above fiddle from Miro. Please check my updated version here
var canvas = $('#canvas');
var paper = Raphael(canvas.attr('id'), canvas.width(), canvas.height());
var mouseDownX = 0;
var mouseDownY = 0;
var elemClicked;
var shap;
var borderColor = '#093';
var fillColor = '#eee';
var option = 1;
var shapOpacity = .5;
var ShapType = {RECTANGLE: 1, CIRCLE: 2, LINE: 3}
$(document).ready(function() {
$("#action").change(function() {
option = $('option:selected', this).val();
});
$("#start").click(function(e) {
reset();
$(canvas).mousedown(function(e) {
e.originalEvent.preventDefault();
var offset = $(canvas).offset();
mouseDownX = e.pageX - offset.left;
mouseDownY = e.pageY - offset.top;
if(option == ShapType.RECTANGLE){
shap = drawRectangle(mouseDownX, mouseDownY, 0, 0);
}
else if(option == ShapType.CIRCLE){
shap = drawCircle(mouseDownX, mouseDownY, mouseDownX, mouseDownY);
}else if(option == ShapType.LINE){
shap = drawLine(mouseDownX, mouseDownY, mouseDownX, mouseDownY);
}
$(canvas).mousemove(function(e) {
var offset = $(canvas).offset();
var upX = e.pageX - offset.left;
var upY = e.pageY - offset.top;
var width = upX - mouseDownX;
var height = upY - mouseDownY;
if(option == ShapType.RECTANGLE){
shap.attr( { "width": width > 0 ? width : 0,
"height": height > 0 ? height : 0 } );
}else if(option == ShapType.CIRCLE || option == ShapType.LINE){
shap.updateEnd(upX, upY);
}
}); // end mouse down.
});// end mouse down.
$(canvas).mouseup(function(e) {
$(canvas).unbind('mousemove');
if(option == ShapType.RECTANGLE){
var BBox = shap.getBBox();
if ( BBox.width == 0 && BBox.height == 0 ) shap.remove();
}
}); // end mouse up.
}); // end document ready.
$("#done").click(function(e) {
$(canvas).unbind('mouseup');
$(canvas).unbind('mousemove');
$(canvas).unbind('mousedown');
});
$("#clear").click(function(e) {
$(canvas).find('rect, circle, path').each(function(i, obj) {
$(this).remove();
});
});
$("#deleteshap").click(function(e) {
$('#' + elemClicked).remove();
});
var start = function() {
this.ox = this.attr("x");
this.oy = this.attr("y");
this.attr({
opacity: shapOpacity
});
this.ow = this.attr('width');
this.oh = this.attr('height');
}
var move = function(dx, dy) {
nowX = Math.min(paper.width, this.ox + dx);
nowY = Math.min(paper.height, this.oy + dy);
nowX = Math.max(0, nowX);
nowY = Math.max(0, nowY);
this.attr({
x: nowX,
y: nowY
});
if (this.attr("fill") != fillColor) this.attr({
fill: fillColor
});
}
var up = function() {
this.attr({
opacity: shapOpacity
});
if (this.attr("y") < 60 && this.attr("x") < 60) this.attr({
fill: fillColor
});
};
var reset = function(){
$(canvas).unbind('mousedown');
$(canvas).unbind('mousemove');
$(canvas).unbind('mouseup');
}
function drawLine(startX, startY, endX, endY) {
var start = {
x: startX,
y: startY
};
var end = {
x: endX,
y: endY
};
var getPath = function() {
return "M" + start.x + " " + start.y + " L" + end.x + " " + end.y;
};
var redraw = function() {
node.attr("path", getPath());
node.attr({
stroke: borderColor
});
}
var node = paper.path(getPath());
$(node.node).attr('id', 'shap' + startX + startY);
node.click(function(e) {
elemClicked = $(node.node).attr('id');
});
return {
updateStart: function(x, y) {
start.x = x;
start.y = y;
redraw();
return this;
},
updateEnd: function(x, y) {
end.x = x;
end.y = y;
redraw();
return this;
}
};
};
function drawRectangle(x, y, w, h) {
var element = paper.rect(x, y, w, h);
element.attr({
fill: fillColor,
opacity: shapOpacity,
stroke: borderColor
});
$(element.node).attr('id', 'shap' + x + y);
element.drag(move, start, up);
element.click(function(e) {
elemClicked = $(element.node).attr('id');
});
return element;
}
function drawCircle(x1, y1, x2, y2)
{
var center = {
x: (x1+x2)/2,
y: (y1+y2)/2
};
var radius = {
h: Math.sqrt((y2 - y1) * (y2 - y1))/2,
w: Math.sqrt((x2 - x1) * (x2 - x1))/2
};
var getPath = function () {
return [["M", center.x, center.y], ["m", 0, -radius.h],
["a", radius.w, radius.h, 0, 1, 1, 0, 2 * radius.h],
["a", radius.w, radius.h, 0, 1, 1, 0, -2 * radius.h],
["z"]];
};
var redraw = function () {
node.attr("path", getPath());
node.attr(
{ fill: fillColor,
stroke: borderColor,
});
};
var node = paper.path(getPath());
$(node.node).attr('id', 'shap' + x1 + y1);
node.click(function(e) {
elemClicked = $(node.node).attr('id');
});
dragCircle(node);
return {
updateStart: function (x, y) {
center.x = (x1 + x) / 2;
center.y = (y1 + y) / 2;
radius.w = Math.sqrt((x - x1) * (x - x1))/2;
radius.h = Math.sqrt((y - y1) * (y - y1))/2;
redraw();
return this;
},
updateEnd: function (x, y) {
center.x = (x1 + x) / 2;
center.y = (y1 + y) / 2;
radius.w = Math.sqrt((x - x1) * (x - x1))/2;
radius.h = Math.sqrt((y - y1) * (y - y1))/2;
redraw();
return this;
}
};
} // end circle
dragCircle = function(node) {
var me = node,
lx = 0,
ly = 0,
ox = 0,
oy = 0,
moveFnc = function(dx, dy) {
lx = dx + ox;
ly = dy + oy;
me.transform('t' + lx + ',' + ly);
},
startFun = function() {},
endFnc = function() {
ox = lx;
oy = ly;
};
node.drag(moveFnc, function() {} , endFnc);
};
});
#canvas{background-color:#eee; background-size:cover; width:400px; height:300px;}
.controls input{background: #15A3D7;border: #eee 1px solid;border-radius: 3px;padding: 3px 12px;margin: 5px 3px;color: #fff;}
.controls select{padding: 3px;background: #eee;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="controls">
<input id="start" value="Start" type="button" />
<input id="done" value="Done" type="button" />
<input id="deleteshap" value="Delete Shap" type="button" />
<input id="clear" value="Clear All" type="button" />
<select id="action">
<option value="1">Rectangle</option>
<option value="2">Circle</option>
<option value="3">Line</option>
</select>
</div>
<div class="dvcls" id="canvas"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
Here is also a good reference to play with Raphael Js.
http://www.ebooksbucket.com/instant-raphaeljs-starter-b184
Hope this will help.