This is the behavior I want to achieve in Vue.js Here is the Js fiddle example i am trying to make: https://jsfiddle.net/richardcwc/ukqhf54k/
//Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//Variables
var canvasx = $(canvas).offset().left;
var canvasy = $(canvas).offset().top;
var last_mousex = last_mousey = 0;
var mousex = mousey = 0;
var mousedown = false;
//Mousedown
$(canvas).on('mousedown', function(e) {
last_mousex = parseInt(e.clientX-canvasx);
last_mousey = parseInt(e.clientY-canvasy);
mousedown = true;
});
//Mouseup
$(canvas).on('mouseup', function(e) {
mousedown = false;
});
//Mousemove
$(canvas).on('mousemove', function(e) {
mousex = parseInt(e.clientX-canvasx);
mousey = parseInt(e.clientY-canvasy);
if(mousedown) {
ctx.clearRect(0,0,canvas.width,canvas.height); //clear canvas
ctx.beginPath();
var width = mousex-last_mousex;
var height = mousey-last_mousey;
ctx.rect(last_mousex,last_mousey,width,height);
ctx.strokeStyle = 'black';
ctx.lineWidth = 10;
ctx.stroke();
}
//Output
$('#output').html('current: '+mousex+', '+mousey+'<br/>last: '+last_mousex+', '+last_mousey+'<br/>mousedown: '+mousedown);
});
I am using a library called Konva.js. Right now I am able to free drawing in Vue.js with Konva.js. But When I try to draw the rectangle with mousemove. It does not work correctly. I am not sure what causes the issue. Thanks for any help! Here is my work on
Code sandbox
This is the behavior I found out for my work. It only draws the rectangle after the mouse move event and then mouse click event.
<template>
<v-stage
ref="stage"
:config="stageSize"
#mousemove="handleMouseMove"
#mouseDown="handleMouseDown"
#mouseUp="handleMouseUp"
>
<v-layer ref="layer">
<v-text
ref="text"
:config="{
x: 10,
y: 10,
fontSize: 20,
text: text,
fill: 'black',
}"
/>
<v-rect
v-for="(rec, index) in recs"
:key="index"
:config="{
x: Math.min(rec.startPointX, rec.startPointX + rec.width),
y: Math.min(rec.startPointY, rec.startPointY + rec.height),
width: Math.abs(rec.width),
height: Math.abs(rec.height),
fill: 'rgb(0,0,0,0)',
stroke: 'black',
strokeWidth: 3,
}"
/>
</v-layer>
</v-stage>
</template>
<script>
const width = window.innerWidth;
const height = window.innerHeight;
export default {
data() {
return {
stageSize: {
width: width,
height: height,
},
text: "Try to draw a rectangle",
lines: [],
isDrawing: false,
recs: [],
};
},
methods: {
handleMouseDown(event) {
this.isDrawing = true;
const pos = this.$refs.stage.getNode().getPointerPosition();
this.setRecs([
...this.recs,
{ startPointX: pos.x, startPointY: pos.y, width: 0, height: 0 },
]);
},
handleMouseUp() {
this.isDrawing = false;
},
setRecs(element) {
this.recs = element;
},
handleMouseMove(event) {
// no drawing - skipping
if (!this.isDrawing) {
return;
}
// console.log(event);
const point = this.$refs.stage.getNode().getPointerPosition();
// handle rectangle part
let curRec = this.recs[this.recs.length - 1];
curRec.width = point.x - curRec.startPointX;
curRec.height = point.y - curRec.startPointY;
},
},
};
</script>
Demo: https://codesandbox.io/s/vue-konva-drawings-rectangles-ivjtu?file=/src/App.vue
After dragging and releasing a shape I want it to snap to a close by position. To test this I create a shape at {x:100, y:100}, then drag it and it does snap to 0,0, but only the first time Im dragging it. Next time it will ignore me setting x,y.
I might be missing something basic here? Maybe I am not mutating store the right way. In the below code you can see three attempts to set x and y in handleDragend.
<template>
<div>
<v-stage
ref="stage"
:config="configKonva"
#dragstart="handleDragstart"
#dragend="handleDragend"
>
<v-layer ref="layer">
<v-regular-polygon
v-for="item in list"
:key="item.id"
:config="{
x: item.x,
y: item.y,
sides: 6,
rotation: item.rotation,
id: item.id,
radius: 50,
outerRadius: 50,
fill: 'green',
draggable: true,
}"
></v-regular-polygon>
</v-layer>
</v-stage>
</div>
</template>
<script>
const width = window.innerWidth;
const height = window.innerHeight;
export default {
data() {
return {
list: [],
dragItemId: null,
configKonva: {
width: width,
height: height,
}
};
},
methods: {
handleDragstart(e) {
//
},
handleDragend(e) {
let item = this.list.find(i => i.id === e.target.id());
let snapTo = { x: 0, y: 0}
// Attempt 1
Vue.set(this.list, 0, {
...item,
x: snapTo.x,
y: snapTo.y,
})
// Attempt 2
this.list = this.list.map(function(shape) {
if(shape.id === item.id) {
return {
...item,
x: snapTo.x,
y: snapTo.y,
}
}
})
},
},
mounted() {
this.list.push({
id: 1,
x: 100,
y: 100,
});
}
};
</script>
vue-konva updates nodes ONLY when you have changes in your template.
On the first snap, the coordinated in the template (and store) are changed from {100, 100} to {0, 0}.
When you drag the node the second time, the store still keeps {0, 0} in memory. So no changes are triggered and the node is not moved back.
There are two ways to solve the issue:
(1) Update Konva node position manually
handleDragend(e) {
let item = this.list.find(i => i.id === e.target.id());
let snapTo = { x: 0, y: 0 };
e.target.position(snapTo);
e.target.getLayer().batchDraw();
Vue.set(this.list, 0, {
...item,
x: snapTo.x,
y: snapTo.y
});
}
(2) Keep the store in sync with node position
You may need to register all position changes into the store:
handleDragMove(e) {
// do this on every "dragmove"
let item = this.list.find(i => i.id === e.target.id());
Vue.set(this.list, 0, {
...item,
x: e.target.x(),
y: e.target.y()
});
}
Demo: https://codesandbox.io/s/nifty-northcutt-v52ue
I created two x,y arrays and then performed two chart using these. I want to write some texts in only Chart red one. And I want to use (x,y) coordinates to determine text region.
I try to Chartjs Global plugin but it causes to writing text all charts. I demand texting only one chart.
I could not succeed in Chartjs mono Plugin in Script.
Please Help Me.
Here is my code:
<!DOCTYPE html>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div class="w3-row">
<div class="w3-col s3"><p></p></div>
<div class="w3-col s6" align="center"><canvas id="myChart" height="120"></canvas></div>
<div class="w3-col s3"><p></p></div>
</div>
<div class="w3-row">
<div class="w3-col s3"><p></p></div>
<div class="w3-col s6" align="center"><canvas id="myTau" height="120"></canvas></div>
<div class="w3-col s3"><p></p></div>
</div>
</body>
</html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<script type="text/javascript">
var xCoord = new Array(1997, 2003, 2005, 2009, 2014, 2018, 2019);
var yCoord = new Array(1, 3, 5, 3, 6, 10, 9);
var c = [];
for (var i = 0; i < xCoord.length; i++) {
var obj = { x: xCoord[i], y: yCoord[i] };
c.push(obj);
}
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'None',
data: c,
borderWidth: 1,
borderColor: "#3e95cd",
fill: false,
cubicInterpolationMode: 'monotone'
}
]
},
options: {
title: {
display: true,
text: 'My Chart'
},
tooltips: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'Year Assembly',
fontSize: 14
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Aquifer Values',
fontSize: 15
}
}]
}
}
});
</script>
<script type="text/javascript">
var xCoord = new Array(1997, 2003, 2005, 2009, 2014, 2018, 2019);
var yCoord = new Array(41, 31, 11, 31, 88, 101, 91);
var c = [];
for (var i = 0; i < xCoord.length; i++) {
var obj = { x: xCoord[i], y: yCoord[i] };
c.push(obj);
}
var ctx = document.getElementById('myTau').getContext('2d');
var myTau = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'None',
data: c,
borderWidth: 1,
borderColor: "#ef1414",
fill: false,
cubicInterpolationMode: 'monotone'
}
]
},
options: {
title: {
display: true,
text: 'My Chart 2'
},
tooltips: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'Year Assembly',
fontSize: 14
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Aquifer Values Corresponding',
fontSize: 15
}
}]
}
}
});
</script>
UPDATE:
Here is another problem:
Is there any way to assign text position via chartjs coordinate system, in stead of "width., height."?
For instance, according to xCoord & yCoord arrays, is it possible to set the position of ctx.fillText x,y coordinates:
In stead of ctx.fillText("s(A)", width * .28, height * .70); can be like this : ctx.fillText("s(A)", 2005, 3); (2015,9) is belong to chartjs coordinate system.
My Aim:
s(A) can be seen on chartjs area at the point of (2005,3)
s(A) can be seen on chartjs area at the point of (2015,9)
My Coordinate Problem Picture
Here is Last Problem Code:
<!DOCTYPE html>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div class="w3-row">
<div class="w3-col s3"><p></p></div>
<div class="w3-col s6" align="center"><canvas id="myChart" height="120"></canvas></div>
<div class="w3-col s3"><p></p></div>
</div>
<div class="w3-row">
<div class="w3-col s3"><p></p></div>
<div class="w3-col s6" align="center"><canvas id="myTau" height="120"></canvas></div>
<div class="w3-col s3"><p></p></div>
</div>
</body>
</html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<script type="text/javascript">
var plugin = {
beforeDraw: function (chart) {
var width = chart.chart.width,
height = chart.chart.height,
ctx = chart.chart.ctx;
ctx.restore();
ctx.font = "1em sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("s(A)", width * .28, height * .70);
ctx.fillText("s(B)", width * .75, height * .55);
//These section were set according to canvas width and height
//I want to set coordinates chartjs coordinates like in data {x:1993,y:70}
// Doesnt Work Like This: ctx.fillText("s(A)", 2005, 2);
//Doesnt Work Like This: ctx.fillText("s(B)", 2015, 9);
ctx.save();
}
};
Chart.plugins.register(plugin);
var xCoord = new Array(1997, 2003, 2005, 2009, 2014, 2018, 2019);
var yCoord = new Array(1, 3, 5, 3, 6, 10, 9);
var c = [];
for (var i = 0; i < xCoord.length; i++) {
var obj = { x: xCoord[i], y: yCoord[i] };
c.push(obj);
}
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'None',
data: c,
borderWidth: 1,
borderColor: "#3e95cd",
fill: false,
cubicInterpolationMode: 'monotone'
}
]
},
options: {
plugins: [plugin],
title: {
display: true,
text: 'My Chart'
},
tooltips: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'Year Assembly',
fontSize: 14
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Aquifer Values',
fontSize: 15
}
}]
}
}
});
</script>
An working example:
var plugin = {
id: 'plugin',
beforeDraw: function(chart) {
var width = chart.chart.width,
height = chart.chart.height,
ctx = chart.chart.ctx,
xScale = chart.scales['x-axis-0'],
yScale = chart.scales['y-axis-0'];
ctx.restore();
ctx.font = "1em sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// ctx.fillText("s(A)", width * .28, height * .70);
ctx.fillText(
"s(A)",
xScale.getPixelForValue('2005'),
yScale.getPixelForValue(3)
);
ctx.fillText(
"s(B)",
xScale.getPixelForValue('2015'),
yScale.getPixelForValue(9)
);
ctx.save();
}
};
var xCoord = new Array(1997, 2003, 2005, 2009, 2014, 2018, 2019);
var yCoord = new Array(1, 3, 5, 3, 6, 10, 9);
var c = [];
for (var i = 0; i < xCoord.length; i++) {
var obj = {
x: xCoord[i],
y: yCoord[i]
};
c.push(obj);
}
var ctx = document.getElementById('myTau').getContext('2d');
var myTau = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'None',
data: c,
borderWidth: 1,
borderColor: "#ef1414",
fill: false,
cubicInterpolationMode: 'monotone'
}]
},
plugins: [plugin],
options: {
title: {
display: true,
text: 'My Chart 2'
},
tooltips: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'Year Assembly',
fontSize: 14
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Aquifer Values Corresponding',
fontSize: 15
}
}]
}
}
});
var ctx = document.getElementById('myTax').getContext('2d');
var myTau = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'None',
data: c,
borderWidth: 1,
borderColor: "#ef1414",
fill: false,
cubicInterpolationMode: 'monotone'
}]
},
options: {
title: {
display: true,
text: 'My Chart 2'
},
tooltips: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'Year Assembly',
fontSize: 14
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Aquifer Values Corresponding',
fontSize: 15
}
}]
}
}
});
<canvas id="myTau" height="120"></canvas>
<canvas id="myTax" height="120"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
Create and register a new plugin that draw the text, then call it only on the second chart, you can pass data as well (like an array of labels and the locations of each for example).
Update: (register plugins)
If you register a plugin globally then, you will have to disable the plugin in each chart here you don't want it to run.
options: {
plugins: {
plugin: false
}
}
- or -
If you don't register the plugin globally, in each chart that you need to add the labels add the following:
{
plugins: [plugin]
}
Note: In the second snippet of code plugin is not nested under options. Seen here.
Update: (display text using x,y datasets)
In order to use a x,y value you need to identify the axes you what the value from using its id, the defaults are x-axis-0 and y-axis-0, that increments if you add more axes. Or use a custom axis id.
After that, within the chart instance there is a scale object that represents the axis and using chart.scales['x-axis-0'] you can access a function called getPixelForValue which will convert the giving value from that axis to its pixel location.
<!DOCTYPE html>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div class="w3-row">
<div class="w3-col s3"><p></p></div>
<div class="w3-col s6" align="center"><canvas id="myChart" height="120"></canvas></div>
<div class="w3-col s3"><p></p></div>
</div>
<div class="w3-row">
<div class="w3-col s3"><p></p></div>
<div class="w3-col s6" align="center"><canvas id="myTau" height="120"></canvas></div>
<div class="w3-col s3"><p></p></div>
</div>
</body>
</html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<script type="text/javascript">
var plugin = {
beforeDraw: function (chart) {
var width = chart.chart.width,
height = chart.chart.height,
ctx = chart.chart.ctx;
ctx.restore();
ctx.font = "1em sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("s(A)", width * .28, height * .70);
ctx.fillText("s(B)", width * .75, height * .55);
ctx.save();
}
};
Chart.plugins.register(plugin);
var xCoord = new Array(1997, 2003, 2005, 2009, 2014, 2018, 2019);
var yCoord = new Array(1, 3, 5, 3, 6, 10, 9);
var c = [];
for (var i = 0; i < xCoord.length; i++) {
var obj = { x: xCoord[i], y: yCoord[i] };
c.push(obj);
}
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'None',
data: c,
borderWidth: 1,
borderColor: "#3e95cd",
fill: false,
cubicInterpolationMode: 'monotone'
}
]
},
options: {
plugins: [plugin],
title: {
display: true,
text: 'My Chart'
},
tooltips: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'Year Assembly',
fontSize: 14
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Aquifer Values',
fontSize: 15
}
}]
}
}
});
</script>
<script type="text/javascript">
var xCoord = new Array(1997, 2003, 2005, 2009, 2014, 2018, 2019);
var yCoord = new Array(41, 31, 11, 31, 88, 101, 91);
var c = [];
for (var i = 0; i < xCoord.length; i++) {
var obj = { x: xCoord[i], y: yCoord[i] };
c.push(obj);
}
var ctx = document.getElementById('myTau').getContext('2d');
var myTau = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'None',
data: c,
borderWidth: 1,
borderColor: "#ef1414",
fill: false,
cubicInterpolationMode: 'monotone'
}
]
},
options: {
title: {
display: true,
text: 'My Chart 2'
},
tooltips: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'Year Assembly',
fontSize: 14
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Aquifer Values Corresponding',
fontSize: 15
}
}]
}
}
});
</script>
Here is problem. The text is written all charts. My aim is only red one chart (My Chart 2).
I am using vue-konva with vuejs and I can't appy konva filters on my elements.
I have implemented my component like this :
<template>
<div>
<v-stage ref="stage" :config="configKonva">
<v-layer ref="layer">
<v-image ref="maskelement" :config="imageConfig" />
<v-circle :config="configCircle"></v-circle>
</v-layer>
</v-stage>
</div>
</template>
<script>
export default {
name: 'mySvg',
data() {
const img = new Image();
img.src = 'myImagePath.png';
return {
configCircle: {
x: 500,
y: 200,
radius: 70,
fill: 'red',
stroke: 'pink',
strokeWidth: 4,
},
imageConfig: {
x: 0,
y: 0,
image: img,
width: 1181,
height: 1181,
filters: [
Konva.Filters.Mask,
],
threshold: 200,
},
};
},
};
</script>
I have also tryed to add a sceneFunc attribute to imageConfig like this :
imageConfig: {
x: 0,
y: 0,
image: img,
width: 1181,
height: 1181,
sceneFunc: (context, elem) => {
elem.filters([Konva.Filters.Mask]);
elem.threshold(200);
},
},
But as soon as there is a sceneFunc attribute, my component won't display
How should I use filters with vuejs?
You can use this code to apply cache to the image:
<template>
<v-stage :config="{
width: 300,
height: 300
}">
<v-layer>
<v-image :config="{
image: this.image,
filters: this.filters,
blur: 100,
scaleX: 0.3,
scaleY: 0.3
}" ref="image"></v-image>
</v-layer>
</v-stage>
</template>
<script>
import VueKonva from 'vue-konva'
import Vue from 'vue';
Vue.use(VueKonva)
export default {
name: "App",
data() {
return {
image: null,
filters: [Konva.Filters.Blur]
}
},
created() {
var img = new Image();
img.src = './logo.png';
img.onload = () => {
this.image = img;
this.$nextTick(() => {
const node = this.$refs.image.getStage();
node.cache();
node.getLayer().batchDraw();
})
}
}
};
</script>