I have a simple linear chart built with Chart.js library.
And i want to allow user to drag points on chart for dynamically change data of it. I tied chartjs-plugin-draggable but it works for me only with annotations. I need graph exactly like this:
https://www.rgraph.net/canvas/docs/adjusting-line.html
But use new graph library in project is not good solution :(
Also i tried to play with dot event's.
UPDATE:
With angular i created something like this.
Maybe if there is no way to add drag&drop to points, there will be a hack to put "sliders" with absolute position on graph on points positions. I didn't find any info too :(
In case anyone is looking for a solution that doesn't require the use of plugins, it's pretty straightforward to do it in vanilla chart.js.
Here's a simple working example - just click and drag a data point
// some data to be plotted
var x_data = [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050];
var y_data_1 = [86,114,106,106,107,111,133,221,783,2478];
var y_data_2 = [2000,700,200,100,100,100,100,50,25,0];
// globals
var activePoint = null;
var canvas = null;
// draw a line chart on the canvas context
window.onload = function () {
// Draw a line chart with two data sets
var ctx = document.getElementById("canvas").getContext("2d");
canvas = document.getElementById("canvas");
window.myChart = Chart.Line(ctx, {
data: {
labels: x_data,
datasets: [
{
data: y_data_1,
label: "Data 1",
borderColor: "#3e95cd",
fill: false
},
{
data: y_data_2,
label: "Data 2",
borderColor: "#cd953e",
fill: false
}
]
},
options: {
animation: {
duration: 0
},
tooltips: {
mode: 'nearest'
}
}
});
// set pointer event handlers for canvas element
canvas.onpointerdown = down_handler;
canvas.onpointerup = up_handler;
canvas.onpointermove = null;
};
function down_handler(event) {
// check for data point near event location
const points = window.myChart.getElementAtEvent(event, {intersect: false});
if (points.length > 0) {
// grab nearest point, start dragging
activePoint = points[0];
canvas.onpointermove = move_handler;
};
};
function up_handler(event) {
// release grabbed point, stop dragging
activePoint = null;
canvas.onpointermove = null;
};
function move_handler(event)
{
// locate grabbed point in chart data
if (activePoint != null) {
var data = activePoint._chart.data;
var datasetIndex = activePoint._datasetIndex;
// read mouse position
const helpers = Chart.helpers;
var position = helpers.getRelativePosition(event, myChart);
// convert mouse position to chart y axis value
var chartArea = window.myChart.chartArea;
var yAxis = window.myChart.scales["y-axis-0"];
var yValue = map(position.y, chartArea.bottom, chartArea.top, yAxis.min, yAxis.max);
// update y value of active data point
data.datasets[datasetIndex].data[activePoint._index] = yValue;
window.myChart.update();
};
};
// map value to other coordinate system
function map(value, start1, stop1, start2, stop2) {
return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1))
};
body {
font-family: Helvetica Neue, Arial, sans-serif;
text-align: center;
}
.wrapper {
max-width: 800px;
margin: 50px auto;
}
h1 {
font-weight: 200;
font-size: 3em;
margin: 0 0 0.1em 0;
}
h2 {
font-weight: 200;
font-size: 0.9em;
margin: 0 0 50px;
color: #555;
}
a {
margin-top: 50px;
display: block;
color: #3e95cd;
}
<!DOCTYPE html>
<html>
<!-- HEAD element: load the stylesheet and the chart.js library -->
<head>
<title>Draggable Points</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<!-- BODY element: create a canvas and render a chart on it -->
<body>
<!-- canvas element in a container -->
<div class="wrapper">
<canvas id="canvas" width="1600" height="900"></canvas>
</div>
<!-- call external script to create and render a chart on the canvas -->
<script src="script.js"></script>
</body>
</html>
Update: My previous answer got deleted because it only featured a link to a plugin solving the issue, however here comes the explanation to what it does:
The general procedure on how to achieve the desired behaviour is to
Intercept a mousedown (and check if it's a dragging gesture) on a given chart
Check if the mousedown was over a data point using the getElementAtEvent function
On mousemove, translate the new Y-Pixel value into a data coordinate using the axis.getValueForPixel function
Synchronously update the chart data using chart.update(0)
as pointed out in this Chart.js issue.
In order to intercept the mousedown, mousemove and mouseup events (the dragging gesture), event listeners for said events need to be created. In order to simplify the creation of the listeners one may use the d3 library in this case as follows:
d3.select(chartInstance.chart.canvas).call(
d3.drag().container(chartInstance.chart.canvas)
.on('start', getElement)
.on('drag', updateData)
.on('end', callback)
);
On mousedown (the 'start' event here), a function (getElement) may be called thatfetches the closest chart element to the pointers location and gets the ID of the Y-Scale
function getElement () {
var e = d3.event.sourceEvent
element = chartInstance.getElementAtEvent(e)[0]
scale = element['_yScale'].id
}
On mousemove ('drag'), the chart data is supposed to be updated according to the current Y-Pixel value of the pointer. We can therefore create an updateData function that gets the position of the clicked data point in the charts data array and the according dataset like this
function updateData () {
var e = d3.event.sourceEvent
var datasetIndex = element['_datasetIndex']
var index = element['_index']
var value = chartInstance.scales[scale].getValueForPixel(e.clientY)
chartInstance.data.datasets[datasetIndex].data[index] = value
chartInstance.update(0)
}
And that's it! If you need to store the resulting value after dragging, you may also specify a callback function like this
function callback () {
var datasetIndex = element['_datasetIndex']
var index = element['_index']
var value = chartInstance.data.datasets[datasetIndex].data[index]
// e.g. store value in database
}
Here is a working fiddle of the above code. The functionality is also the core of the Chart.js Plugin dragData, which may be easier to implement in many cases.
Here is how I fixed using both touchscreen or mouse event x,y coordinates for the excellent d3 example above by wrapping event screen coordinates in a more "generic" x,y object.
(Probably d3 has something similar to handle both types of events but lot of reading to find out..)
//Get an class of {points: [{x, y},], type: event.type} clicked or touched
function getEventPoints(event)
{
var retval = {point: [], type: event.type};
//Get x,y of mouse point or touch event
if (event.type.startsWith("touch")) {
//Return x,y of one or more touches
//Note 'changedTouches' has missing iterators and can not be iterated with forEach
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches.item(i);
retval.point.push({ x: touch.clientX, y: touch.clientY })
}
}
else if (event.type.startsWith("mouse")) {
//Return x,y of mouse event
retval.point.push({ x: event.layerX, y: event.layerY })
}
return retval;
}
.. and here is how I would use it in the above d3 example to store the initial grab point Y. And works for both mouse and touch.
Check the Fiddle
Here how I solved the problem with using d3 and wanting to drag the document on mobile or touch screens. Somehow with the d3 event subscription all Chart area events where already blocked from bubbling up the DOM.
Was not able to figure out if d3 could be configured to pass canvas events on without touching them. So in a protest I just eliminated d3 as it was not much involved other than subscribing events.
Not being a Javascript master this is some fun code that subscribes the events the old way. To prevent chart touches from dragging the screen only when a chart point is grabed each of the handlers just have to return true and the event.preventDefault() is called to keep the event to your self.
//ChartJs event handler attaching events to chart canvas
const chartEventHandler = {
//Call init with a ChartJs Chart instance to apply mouse and touch events to its canvas.
init(chartInstance) {
//Event handler for event types subscribed
var evtHandler =
function myeventHandler(evt) {
var cancel = false;
switch (evt.type) {
case "mousedown":
case "touchstart":
cancel = beginDrag(evt);
break;
case "mousemove":
case "touchmove":
cancel = duringDrag(evt);
break;
case "mouseup":
case "touchend":
cancel = endDrag(evt);
break;
default:
//handleDefault(evt);
}
if (cancel) {
//Prevent the event e from bubbling up the DOM
if (evt.cancelable) {
if (evt.preventDefault) {
evt.preventDefault();
}
if (evt.cancelBubble != null) {
evt.cancelBubble = true;
}
}
}
};
//Events to subscribe to
var events = ['mousedown', 'touchstart', 'mousemove', 'touchmove', 'mouseup', 'touchend'];
//Subscribe events
events.forEach(function (evtName) {
chartInstance.canvas.addEventListener(evtName, evtHandler);
});
}
};
The handler above is initiated like this with an existing Chart.js object:
chartEventHandler.init(chartAcTune);
The beginDrag(evt), duringDrag(evt) and endDrag(evt) have the same basic function as in the d3 example above. Just returns true when wanting to consume the event and not pasing it on for document panning and similar.
Try it in this Fiddle using a touch screen. Unless you touch close to select a chart point the rest of the chart will be transparent to touch/mouse events and allow panning the page.
Related
I'm trying to use Highcharts Synchronization for a slightly complex chart. I've gotten synchronization to work just fine on graphs with just 1 series for other plots.
You can check out the example here: http://jsfiddle.net/nhng5827/o9uh2jqy/42/
I've dumbed down my complex chart to just a few points, and usually the chart has 6 lines as opposed to 4. As you can see, the tooltip only appears on the right side of the graph, and everything is delayed. I also do not want the callout box to have any numbers read out if the tooltip is not hovered the specific lines.
I've done a fair bit of research and have implemented some code I've seen that allows for charts with 2 series to be synchronized (http://jsfiddle.net/mjsdnngq/72/), however I think the biggest difference in mine is that the series only go to about half of the actual chart.
$('#container').bind('mousemove touchmove touchstart', function (e) {
var chart,
points,
i,
secSeriesIndex = 1;
for (i = 0; i < Highcharts.charts.length; i++) {
chart = Highcharts.charts[i];
e = chart.pointer.normalize(e); // Find coordinates within the chart
points = [chart.series[0].searchPoint(e, true), chart.series[1].searchPoint(e, true),chart.series[2].searchPoint(e, true),chart.series[3].searchPoint(e, true)]; // Get the hovered point
if (points[0] && points[1]&& points[2]&& points[3]) {
// if (!points[0].series.visible) {
// points.shift();
// secSeriesIndex = 0;
// }
// if (!points[secSeriesIndex].series.visible) {
// points.splice(secSeriesIndex,1);
// }
if (points.length) {
chart.tooltip.refresh(points); // Show the tooltip
chart.xAxis[0].drawCrosshair(e, points[0]); // Show the crosshair
chart.xAxis[0].drawCrosshair(e, points[3]); // Show the crosshair
}
}
}
});
/**
* Override the reset function, we don't need to hide the tooltips and crosshairs.
*/
Highcharts.Pointer.prototype.reset = function () {
return undefined;
};
Highcharts.Point.prototype.highlight = function (event) {
this.onMouseOver(); // Show the hover marker
this.series.chart.tooltip.refresh([points[0],points[1],points[2],points[3]]); // Show the tooltip
this.series.chart.xAxis[0].drawCrosshair(event, this); // Show the crosshair
};
/**
* Synchronize zooming through the setExtremes event handler.
*/
function syncExtremes(e) {
var thisChart = this.chart;
if (e.trigger !== 'syncExtremes') { // Prevent feedback loop
Highcharts.each(Highcharts.charts, function (chart) {
if (chart !== thisChart) {
if (chart.xAxis[0].setExtremes) { // It is null while updating
chart.xAxis[0].setExtremes(e.min, e.max, undefined, false, { trigger: 'syncExtremes' });
}
}
});
}
}
Any help would be immensely appreciated.
To correctly synchronize charts, you have to clear points state on every mousemove event:
Highcharts.each(chart.series, function(s) {
Highcharts.each(s.points, function(p) {
p.setState('');
});
});
Live demo: http://jsfiddle.net/BlackLabel/cvb6pwsu/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Point#setState
Also, please take a look at this example: http://jsfiddle.net/BlackLabel/6f37b42q/, it can be useful for you.
I am trying to display the mouse cursors of all the connected client screen on every client's screen. Something like this : http://www.moock.org/unity/clients/uCoop/uCoop.html
I am working on socket.io using node.js.
I tried drawing a circle on the cursor position on the screen using context.drawImage on mousemove but the cursor remains on the screen even after the mouse moves away and clearing the screen makes it slow. So I think, drawing on a canvas is not a perfect solution, I just need to emit the information of mouse co-ordinates to the client somehow. But I don't know how.
Client side code snippet:
socket.on('draw_cursor', function (data) {
var line = data.line;
context.beginPath();
context.fillStyle = "#000000";
context.arc(line[0].x*width, line[0].y*height, 10, 0, 2*Math.PI);
context.fill();
delay(2000);
});
function mainLoop() {
if (mouse.move && mouse.pos_prev) {
// send line to to the server
socket.emit('draw_cursor', { line: [ mouse.pos, mouse.pos_prev ] });
}
}
Server side code snippet:
socket.on('draw_cursor', function (data) {
io.emit('draw_cursor', { line: data.line });
});
Thanks
Vinni
I propose you draw HTML elements instead of using a canvas. That way, you can reuse the same element for each cursor and just update the coordinates. To do this, you should add an ID to each draw_cursor message, to keep track of which element is which:
socket.on('draw_cursor', function (data) {
io.emit('draw_cursor', { line: data.line, id: socket.id });
});
Then, in your client handler, you find or create the HTML element and update it's position:
function getCursorElement (id) {
var elementId = 'cursor-' + id;
var element = document.getElementById(elementId);
if(element == null) {
element = document.createElement('div');
element.id = elementId;
element.className = 'cursor';
// Perhaps you want to attach these elements another parent than document
document.appendChild(element);
}
return element;
}
socket.on('draw_cursor', function (data) {
var el = getCursorElement(data.id);
el.style.x = data.line[0].x;
el.style.y = data.line[0].y;
}
Now, you just have to style the cursor elements. Here's a little css to start with:
.cursor {
position: absolute;
background: black;
width: 20px;
height: 20px;
border-radius: 10px;
}
I tried asking this question before but the way I asked it was so confusing that I didn't get any help. I originally thought it was React to blame for my touchmove events to ceasefire when updating subcomponents. I now am pretty sure it is the Chartist.js library, or possibly how I'm wrapping chartist into a react component, that is stopping the action.
Instead of rambling on about my question I've created two JSfiddles. One that shows you can create a React slider that updates it's values continuously, regardless of being called from mousemove or touchmove.
http://jsfiddle.net/higginsrob/uf6keps2/
// please follow the link for full example
The Second fiddle implements my react wrapper for chartist, and a simplified example of how I'm using it. When you click/drag on the chart it will select the data point at the current x value. This is working fine with a mouse, but trying it on mobile touch devices (or chrome's mobile emulator) it will only fire a few times, and only update the chart once.
http://jsfiddle.net/higginsrob/Lpcg1c6w/
// please follow the link for full example
Any help is appreciated!
Ok, so you need to put a transparent div in front of the chartist chart that captures the mousedown/touchstart, mousemove/touchmove, and mouseup/touchend events.
working example:
http://jsfiddle.net/higginsrob/jwhbzgrb/
// updated event functions:
onTouchStart: function (evt) {
evt.preventDefault();
this.is_touch = (evt.touches);
var node = evt.currentTarget.previousSibling;
var grid = node.querySelector('.ct-grids');
var bbox = grid.getBBox();
this.columnwidth = bbox.width / this.props.data.length;
this.offset = this.getScrollLeftOffset(node) + bbox.x + (this.columnwidth / 2);
this.istouching = true;
this.onTouchMove(evt);
}
onTouchMove: function (evt) {
if(this.istouching){
var x;
if (this.is_touch) {
if(evt.touches && evt.touches[0]){
x = evt.touches[0].clientX - this.offset;
}
} else {
x = evt.clientX - this.offset;
}
this.setState({
index: Math.round(x / this.columnwidth)
});
}
}
onTouchEnd: function(evt){
this.istouching = false;
}
// updated render function:
render: function () {
return React.DOM.div(
{
style: {
position: "relative"
}
},
ReactChartist({ ... your chartist chart here .... }),
// this div sits in front of the chart and captures events
React.DOM.div({
onMouseDown: this.onTouchStart,
onTouchStart: this.onTouchStart,
onMouseMove: this.onTouchMove,
onTouchMove: this.onTouchMove,
onMouseUp: this.onTouchEnd,
onTouchEnd: this.onTouchEnd,
style: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0
}
})
);
}
I wonder if it's possible to build a 3d donut chart in html.
I have found a interesting link here but infortunatly i need to add links (or javascript event) when clicking to launch a ajax request.
Have you ever done such a thing ?
Thanks for your answers
See the following example I've just made:
http://jsfiddle.net/baQCD/3/embedded/result/
The key point (pun intended) is to add a url key for each row (object) in the data array, and use it in the 'click' event handler:
point: {
events: {
click: function(e) {
location.href = e.point.url;
e.preventDefault();
}
}
},
In your case instead of opening a new url, you could do your ajax request or do anything else. In my example I've shown how to manipulate the data and title.
click: function(e) {
if (this.name == "Randomize!") {
sliceK = getRandomInt(0,chart.series[0].data.length-1);
chart.options.series[0].data[sliceK].y = getRandomInt(1,30);
chart = new Highcharts.Chart(chart.options);
} else if (this.name == "Link") {
location.href = this.url;
e.preventDefault();
} else {
chart.setTitle(null,{text:this.name + " clicked"});
}
}
You can immediately see, 2 features I very like in Highcharts, the ability to print or download the chart, and the ability to disable part of the data (removing it from the chart) by clicking on the legend.
This is based on the code shown in:
http://birdchan.com/home/2012/09/07/highcharts-pie-charts-can-have-url-links/
http://www.highcharts.com/demo/3d-pie-donut/
this is a simple 3d Axonometric class i wrote for testing, its very simple it puts the canvas transformation into a plane of zy or zx or yx... it uses canvas setTransform
you first have to call the axionometric class with phi and theta the angles of view
get_bd is a function where you can enter x,y,z coordinates and the method returns an object with b and d value... b is the x of the screen and d is the y of the screen.
i have appended and example, you just have to put a canvas tag in the html with id canvasView
//3d Maths - Axonometric -- Artner Thorsten -- Austria -- Wiener Neustadt
var context=document.getElementById("canvasView").getContext("2d");
function Axonometric (phi,theta)
{
var cosPHI=Math.cos(phi);
var sinPHI=Math.sin(phi);
var cosTHETA=Math.cos(theta);
var sinTHETA=Math.sin(theta);
this.cosPHI=cosPHI;
this.sinPHI=sinPHI;
this.cosTHETA=cosTHETA;
this.sinTHETA=sinTHETA;
this.phi=phi;
this.theta=theta;
}
Axonometric.prototype.get_bd=function (x,y,z)
{
var b=y*this.cosPHI-x*this.sinPHI-500;
var d=x*this.cosPHI*this.cosTHETA+y*this.sinPHI*this.cosTHETA-z*this.sinTHETA+500;
return {b:b,d:d};
}
Axonometric.prototype.plane_zy=function (x)
{
context.setTransform (0,this.sinTHETA,-this.cosPHI,this.sinPHI*this.cosTHETA,500+x*this.sinPHI,500+x*this.cosPHI*this.cosTHETA);
}
Axonometric.prototype.plane_zx=function (y)
{
context.setTransform (this.sinPHI,this.cosPHI*this.cosTHETA,0,this.sinTHETA,500+y*-this.cosPHI,500+y*this.sinPHI*this.cosTHETA);
}
Axonometric.prototype.plane_yx=function (z)
{
context.setTransform (this.sinPHI,this.cosPHI*this.cosTHETA,-this.cosPHI,this.sinPHI*this.cosTHETA,500,500-z*this.sinTHETA);
}
Axonometric.prototype.draw_axis=function (length)
{
var O=this.get_bd (0,0,0);
var X=this.get_bd (length,0,0);
var Y=this.get_bd (0,length,0);
var Z=this.get_bd (0,0,length);
context.save;
context.beginPath ();
context.textAlign="top";
context.fillText ("X",-X.b,X.d);
context.moveTo (-O.b,O.d);
context.lineTo (-X.b,X.d);
context.strokeStyle="red";
context.stroke ();
context.beginPath ();
context.fillText ("Y",-Y.b,Y.d);
context.moveTo (-O.b,O.d);
context.lineTo (-Y.b,Y.d);
context.strokeStyle="green";
context.stroke ();
context.beginPath ();
context.fillText ("Z",-Z.b,Z.d);
context.moveTo (-O.b,O.d);
context.lineTo (-Z.b,Z.d);
context.strokeStyle="blue";
context.stroke ();
context.restore ();
}
// example
var Viewer=new Axonometric (Math.PI/4, Math.PI/8);
Viewer.draw_axis (400);
Viewer.plane_yx (0);
context.beginPath ();
context.fillStyle="red";
context.fillRect (0,0,200,200);
Viewer.plane_zx (0);
context.beginPath ();
context.fillStyle="lightgrey";
context.fillRect (0,0,200,-200);
Viewer.plane_zy (0);
context.beginPath ();
context.arc (-100,100,100,0,2*Math.PI);
context.fillStyle="black";
context.fill();
Using an existing library is an easy solution. If I'm understanding your question properly, you would like users to be able to click on a slice to open a new URL.
This can be achieved in ZingChart by setting up a "pie3d" type, and then including "url" and "target" in the series.
Here's how I did it:
{
"graphset":[
{
"type":"pie3d",
"plot":{
"slice":45
},
"plotarea":{
"margin-top":"35px"
},
"series":[
{
"text":"Apples",
"values":[5],
"url":"http://www.google.com",
"target":"_blank"
},
{
"text":"Oranges",
"values":[8]
},
{
"text":"Bananas",
"values":[22]
},
{
"text":"Grapes",
"values":[16]
},
{
"text":"Cherries",
"values":[12]
}
]
}
]
}
Expanding on Merrily's answer, you can also use ZingChart's API to track chart interaction and call any functions you like.
var ZCwindow;
function openWindow() {
ZCwindow = window.open("http://zingchart.com/docs/chart-types/pie/", "ZingChart Pie Charts");
}
zingchart.node_click = function(e){
if(e.value == 5) openWindow();
};
You can view a live demo here.
I am part of the ZingChart team. You can reach out to us for assistance via support#zingchart.com
For the past few months I have been working with Google Visualization charts, and I think it may be exactly what you're looking for. Here is the link to the documentation.
This will give you a donut chart (though I am not sure if you can make it 3-D or not, I believe you can) and you can add event handlers for when the user clicks on a slice. Here's what it looks like:
I highly recommend trying the charts, I have found them to be extraordinarily useful. Good luck!
EDIT: My apologies, after re-reading the section on donut charts it appears the new API does not yet support 3-D donut charts. Does it absolutely have to be three-dimensional? If not this is still an excellent choice.
It's not 3D, but you should have a look at chart.js
I'm trying to get mouse zooming via mouse scroll working in Paper.js.
Paper.js doesn't have a wheel event, so I need to construct a Paper.js Point from the native wheel event, with correct coordinates, taking into account any View transformations.
For example, this won't work:
const canvas = document.getElementById('canvas')
const tool = new paper.Tool()
paper.setup(canvas)
paper.view.scale(2) // Zoom the view.
// This gives *wrong* coordinates, since we scaled/zoomed the view
// and it's a native event that doesn't take into account PaperJS
// view transformations.
canvas.addEventListener('wheel', e => {
const point = { x: e.offsetX, y: e.offsetY }
console.log(point)
})
// This gives *correct* coordinates, since it's a PaperJS
// event which takes into account the view transformations.
tool.onMouseUp = e => {
console.log(e.point)
}
canvas[resize] {
width: 100%;
height: 100%;
background-color: pink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.2/paper-core.min.js"></script>
<canvas id="canvas" resize></canvas>
Native events won't have a point property, so we need to create a point value for it:
let point = paper.DomEvent.getPoint(e)
But since we want the point in relation to the canvas:
let point = paper.DomEvent.getOffset(e, canvas)
With this we can then convert to project space using view.viewToProject():
point = paper.view.viewToProject(point)
Here's a working example:
const canvas = document.getElementById('canvas')
const tool = new paper.Tool()
paper.setup(canvas)
paper.view.scale(2) // Zoom the view.
// This now gives *correct* points, taking into account
// the view transformations.
canvas.addEventListener('wheel', e => {
const point = paper.view.viewToProject(paper.DomEvent.getOffset(e, canvas))
console.log(point)
})
tool.onMouseUp = e => {
console.log(e.point)
}
canvas[resize] {
width: 100%;
height: 100%;
background-color: pink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.2/paper-core.min.js"></script>
<canvas id="canvas" resize></canvas>