I'm using fabric.JS (custom built to include touch events), I'm facing issue with touch events.
Zoom In/Out is working on mouse wheel but not working on mobile touch screen
I can move canvas using Alt+Left click but on mobile touch screen I'm not able to move canvas
My Working code on codepen Click here
var canvas = new fabric.Canvas('step1');
canvas.add(new fabric.Rect({ width: 50, height: 50, fill: 'blue', angle: 10 }))
canvas.add(new fabric.Circle({ radius: 50, fill: 'red', top: 44, left: 80 }))
canvas.add(new fabric.Ellipse({ rx: 50, ry: 10, fill: 'yellow', top: 80, left: 35 }))
canvas.add(new fabric.Rect({ width: 50, height: 50, fill: 'purple', angle: -19, top: 70, left: 70 }))
canvas.add(new fabric.Circle({ radius: 50, fill: 'green', top: 110, left: 30 }))
canvas.add(new fabric.Ellipse({ rx: 50, ry: 10, fill: 'orange', top: 12, left: 100, angle: 30 }))
//handle Zoom
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var zoom = canvas.getZoom();
zoom *= 0.999 ** delta;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
//handle pan click with Alt key press
canvas.on('mouse:down', function(opt) {
var evt = opt.e;
if (evt.altKey === true) {
this.isDragging = true;
this.selection = false;
this.lastPosX = evt.clientX;
this.lastPosY = evt.clientY;
}
});
canvas.on('mouse:move', function(opt) {
if (this.isDragging) {
var e = opt.e;
var vpt = this.viewportTransform;
vpt[4] += e.clientX - this.lastPosX;
vpt[5] += e.clientY - this.lastPosY;
this.requestRenderAll();
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
}
});
canvas.on('mouse:up', function(opt) {
// on mouse up we want to recalculate new interaction
// for all objects, so we call setViewportTransform
this.setViewportTransform(this.viewportTransform);
this.isDragging = false;
this.selection = true;
});
//handle gustures
var info = document.getElementById('info');
canvas.on({
'touch:gesture': function(e) {
var text = document.createTextNode(' Gesture ');
info.insertBefore(text, info.firstChild);
},
'touch:drag': function(e) {
var text = document.createTextNode(" dragging ");
info.insertBefore(text, info.firstChild);
},
'touch:orientation': function() {
var text = document.createTextNode(' Orientation ');
info.insertBefore(text, info.firstChild);
},
'touch:shake': function() {
var text = document.createTextNode(' Shaking ');
info.insertBefore(text, info.firstChild);
},
'touch:longpress': function() {
var text = document.createTextNode(' Longpress ');
info.insertBefore(text, info.firstChild);
},
});
<div id="info"></div>
<div id="canvasWrapper">
<canvas id="step1" width="400" height="400" style="border: solid gray thin;"></canvas>
</div>
ClientX and ClientY positions for touch devices will not be available in evt object directly. You have to access those from evt.touches
Your updated code may look like
canvas.on('mouse:down', function(opt) {
var evt = opt.e;
this.isDragging = true;
this.selection = false;
if(evt.type === 'mousedown' && evt.altKey === true){
this.lastPosX = evt.clientX;
this.lastPosY = evt.clientY;
}
if(evt.type === 'touchstart' && evt.touches && evt.touches.length === 1){
this.lastPosX = evt.touches[0].clientX;
this.lastPosY = evt.touches[0].clientY;
}
});
canvas.on('mouse:move', function(opt) {
var e = opt.e;
if (this.isDragging) {
if(e.type === 'mousemove'){
vpt[4] += e.clientX - this.lastPosX;
vpt[5] += e.clientY - this.lastPosY;
this.requestRenderAll();
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
}
if(e.type === 'touchmove' && e.touches && e.touches.length){
vpt[4] += e.touches[0].clientX - this.lastPosX;
vpt[5] += e.touches[0].clientY - this.lastPosY;
this.requestRenderAll();
this.lastPosX = e.touches[0].clientX;
this.lastPosY = e.touches[0].clientY;
}
}
});
Related
I have few elements (triangles) and I want to be able to highlight(setShadow) them as I hover above each one (mouse:over/mouse:move), and reset highlight when mouse not over.
I tried this, but it doesn't do anything. Below is my code:
var canvas = new fabric.Canvas('canvas');
var petal1 = new fabric.Triangle({
width: 200,
height: 300,
fill: '#DBDBDB',
left: 500,
top: 350,
angle: 200,
strokeLineJoin: 'round',
strokeWidth: 20,
stroke: '#DBDBDB'
});
var petal2 = new fabric.Triangle({
// same options as above
});
var petal3 = new fabric.Triangle({
// same options as above
});
canvas.on('mouse:over', function(e) {
e.target.set('fill', 'red');
canvas.renderAll();
});
canvas.on('mouse:out', function(e) {
e.target.set('fill', 'green');
canvas.renderAll();
});
canvas.add(petal1, petal2, petal3);
I added console.log(e.target);, but it printed nothing.
Your snippet works and check target before setting property to it.
DEMO
var canvas = new fabric.Canvas('canvas');
var options = {
width: 100,
height: 200,
fill: '#DBDBDB',
strokeLineJoin: 'round',
strokeWidth: 20,
stroke: '#DBDBDB'
};
var petal1 = new fabric.Triangle(options);
var petal2 = new fabric.Triangle(options);
var petal3 = new fabric.Triangle(options);
canvas.on('mouse:over', function(e) {
if(e.target){
e.target.set('fill', 'red');
canvas.requestRenderAll();
}
});
canvas.on('mouse:out', function(e) {
if(e.target){
e.target.set('fill', 'green');
canvas.requestRenderAll();
}
});
canvas.add(petal1, petal2, petal3);
canvas{
border:1px solid;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.7.0/fabric.js"></script>
<canvas id="canvas" width="300" height="300"></canvas>
I have a fabricJS editor with some large svg files, therefore, when I interactive with ( move / drag ) items on the editor I fill it very lag & heavy.
How to optimize using large svg files with fabricJS ?
big_image_1.svg: 4.4MB
big_image_2.svg: 6.1MB
big_image_3.svg: 4.1MB
big_image_4.svg: 13.6MB
Here is my code:
var canvas;
var canvasObject = document.getElementById("editorCanvas");
// set canvas equal size with div
$(canvasObject).width($("#canvasContainer").width());
$(canvasObject).height($("#canvasContainer").height());
canvas = new fabric.Canvas('editorCanvas', {
backgroundColor: 'white',
selectionLineWidth: 2,
width: $("#canvasContainer").width(),
height: $("#canvasContainer").height()
});
for (var i = 0; i < 4; i++) {
var bigImage = new fabric.Image();
bigImage.left = 10 * (i + 1);
bigImage.top = 10 * (i + 1);
bigImage["objectCaching"] = true;
canvas.add(bigImage);
bigImage.setSrc('https://futushigame.firebaseapp.com/big_image/big_image_' + (i + 1) + '.svg', function(imageObject) {
imageObject.set('dirty', true);
canvas.renderAll();
setObjectCoords();
});
}
function setObjectCoords() {
canvas.forEachObject(function(object) {
object.setCoords();
});
}
#canvasContainer {
width: 100%;
height: 100vh;
background-color: gray;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<div id="canvasContainer">
<canvas id="editorCanvas"></canvas>
</div>
Drag items are filling heavy & lag
You can rewrite needsItsOwnCache from fabric.Image class.
var canvasObject = document.getElementById("editorCanvas");
// set canvas equal size with div
$(canvasObject).width($("#canvasContainer").width());
$(canvasObject).height($("#canvasContainer").height());
canvas = new fabric.Canvas('editorCanvas', {
backgroundColor: 'white',
selectionLineWidth: 2,
width: $("#canvasContainer").width(),
height: $("#canvasContainer").height()
});
for (var i = 0; i < 4; i++) {
var bigImage = new fabric.Image();
bigImage.left = 10 * (i + 1);
bigImage.top = 10 * (i + 1);
// bigImage["objectCaching"] = true;
bigImage["ownCaching"] = true;
// bigImage["clipPath"] = [];
canvas.add(bigImage);
bigImage.setSrc('https://futushigame.firebaseapp.com/big_image/big_image_' + (i + 1) + '.svg', function(imageObject) {
//imageObject.set('dirty', true);
canvas.renderAll();
setObjectCoords();
});
}
function setObjectCoords() {
canvas.forEachObject(function(object) {
object.setCoords();
});
}
fabric.Image.prototype.needsItsOwnCache = function() {
return true
}
#canvasContainer {
width: 100%;
height: 100vh;
background-color: gray;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<div id="canvasContainer">
<canvas id="editorCanvas"></canvas>
</div>
I see some solutions but they are not working in V 2.7.2
Is there any way without using animation - make static Y-axis and scroll horizontal
I'm afraid I haven't yet found a way to achieve this without using animation. However, I have managed to find a solution that works in Chart.js 2.7.2.
https://jsfiddle.net/EmmaLouise/eb1aqpx8/3/
This approach handles different DPR settings and will scale the axis to match the scaling that Chart.js applies to its charts. It also calls .clearRect() on the original Y axis that Chart.js draws, clearing the pixels in the defined area which means that there is no duplication of axes or overlaps.
CSS:
.chartWrapper {
position: relative;
}
.chartWrapper > canvas {
position: absolute;
left: 0;
top: 0;
pointer-events: none;
}
.chartAreaWrapper {
width: 600px;
overflow-x: scroll;
}
HTML
<div class="chartWrapper">
<div class="chartAreaWrapper">
<div class="chartAreaWrapper2">
<canvas id="chart-Test" height="300" width="1200"></canvas>
</div>
</div>
<canvas id="axis-Test" height="300" width="0"></canvas>
</div>
JS:
$(function () {
var rectangleSet = false;
var canvasTest = $('#chart-Test');
var chartTest = new Chart(canvasTest, {
type: 'bar',
data: chartData,
maintainAspectRatio: false,
responsive: true,
options: {
tooltips: {
titleFontSize: 0,
titleMarginBottom: 0,
bodyFontSize: 12
},
legend: {
display: false
},
scales: {
xAxes: [{
ticks: {
fontSize: 12,
display: false
}
}],
yAxes: [{
ticks: {
fontSize: 12,
beginAtZero: true
}
}]
},
animation: {
onComplete: function () {
if (!rectangleSet) {
var scale = window.devicePixelRatio;
var sourceCanvas = chartTest.chart.canvas;
var copyWidth = chartTest.scales['y-axis-0'].width - 10;
var copyHeight = chartTest.scales['y-axis-0'].height + chartTest.scales['y-axis-0'].top + 10;
var targetCtx = document.getElementById("axis-Test").getContext("2d");
targetCtx.scale(scale, scale);
targetCtx.canvas.width = copyWidth * scale;
targetCtx.canvas.height = copyHeight * scale;
targetCtx.canvas.style.width = `${copyWidth}px`;
targetCtx.canvas.style.height = `${copyHeight}px`;
targetCtx.drawImage(sourceCanvas, 0, 0, copyWidth * scale, copyHeight * scale, 0, 0, copyWidth * scale, copyHeight * scale);
var sourceCtx = sourceCanvas.getContext('2d');
// Normalize coordinate system to use css pixels.
sourceCtx.clearRect(0, 0, copyWidth * scale, copyHeight * scale);
rectangleSet = true;
}
},
onProgress: function () {
if (rectangleSet === true) {
var copyWidth = chartTest.scales['y-axis-0'].width;
var copyHeight = chartTest.scales['y-axis-0'].height + chartTest.scales['y-axis-0'].top + 10;
var sourceCtx = chartTest.chart.canvas.getContext('2d');
sourceCtx.clearRect(0, 0, copyWidth, copyHeight);
}
}
}
}
});
simple way to achieve this is
make the inner div width dynamically.
so enter image description herebased on the number of bars the width gets changed
Is there a way of adding e.g. circles or boxes to the html5 canvas by using konva.js? i tried it but somehow it doesn't work! What i want it e.g. one click is adding a circle with drag and drop, a doubleclick removes the circle.
This is how it works, maybe someone can use it!
$( document ).ready(function() {
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
var colors = ["red", "orange", "yellow", "green", "blue", "purple"];
var rect1 = new Konva.Rect({
x: 25,
y: 25,
width: width-50,
height: height-50,
opacity: 1,
fill: 'green',
stroke: 'black',
strokeWidth: 2
});
var circle = new Konva.Circle({
x: 230,
y: 100,
radius: 30,
fill: 'grey',
stroke: 'black',
strokeWidth: 1,
draggable: true,
id: 'Origin'
});
circle.on('mouseover', function() {
document.body.style.cursor = 'pointer';
});
circle.on('mouseout', function() {
document.body.style.cursor = 'default';
});
circle.on("dragstart", function () {
this.moveToTop();
layer.draw();
});
circle.on("dragmove", function () {
document.body.style.cursor = "pointer";
});
circle.on("dblclick dbltap", function (evt) {
evt.cancelBubble = true;
console.log("item dblclick");
this.destroy();
layer.draw();
});
circle.on("click tap", function (evt) {
evt.cancelBubble = true;
var node = evt.target;
if (node) {
// update tooltip
var mousePos = node.getStage().getPointerPosition();
console.log("item normal click " + node.getId() + " > node-x" + node.x() +" - fill " + node.getFill());
}
});
rect1.on('dblclick', function() {
var clone;
clone = circle.clone({
x:stage.getPointerPosition().x,
y:stage.getPointerPosition().y,
id: 'myCircle'
});
layer.add(clone);
layer.draw();
console.log('content click on rect1 ' + JSON.stringify(stage.getPointerPosition()));
});
circle.on("mouseover", function () {
document.body.style.cursor = "pointer";
});
circle.on("mouseout", function () {
document.body.style.cursor = "default";
});
layer.add(rect1);
layer.add(circle);
stage.add(layer);
});
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<script src="https://cdn.rawgit.com/konvajs/konva/1.7.6/konva.min.js"></script>
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<meta charset="utf-8">
<title>Add/Remove</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
</style>
</head>
<body>
<div id="container"></div>
</body>
</html>
Im still learning but it seems everything I do does not work in getting the images to also be rotated along with being scaled and dragged. Can someone please help me out with this. Here is the code I am working with...
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body onmousedown="return false;">
<div style="height:1024px; width: 1280px; background-image: url(http://www.designmyprofile.com/images/graphics/backgrounds/background0172.jpg)" id="container"></div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.5.2.min.js"></script>
<script>
function update(activeAnchor) {
var group = activeAnchor.getParent();
var topLeft = group.get('.topLeft')[0];
var topRight = group.get('.topRight')[0];
var bottomRight = group.get('.bottomRight')[0];
var bottomLeft = group.get('.bottomLeft')[0];
var image = group.get('.image')[0];
var anchorX = activeAnchor.getX();
var anchorY = activeAnchor.getY();
// update anchor positions
switch (activeAnchor.getName()) {
case 'topLeft':
topRight.setY(anchorY);
bottomLeft.setX(anchorX);
break;
case 'topRight':
topLeft.setY(anchorY);
bottomRight.setX(anchorX);
break;
case 'bottomRight':
bottomLeft.setY(anchorY);
topRight.setX(anchorX);
break;
case 'bottomLeft':
bottomRight.setY(anchorY);
topLeft.setX(anchorX);
break;
}
image.setPosition(topLeft.getPosition());
var width = topRight.getX() - topLeft.getX();
var height = bottomLeft.getY() - topLeft.getY();
if(width && height) {
image.setSize(width, height);
}
}
function addAnchor(group, x, y, name) {
var stage = group.getStage();
var layer = group.getLayer();
var anchor = new Kinetic.Circle({
x: x,
y: y,
stroke: '#666',
fill: '#ddd',
strokeWidth: 2,
radius: 8,
name: name,
draggable: true,
dragOnTop: false
});
anchor.on('dragmove', function() {
update(this);
layer.draw();
});
anchor.on('mousedown touchstart', function() {
group.setDraggable(false);
this.moveToTop();
});
anchor.on('dragend', function() {
group.setDraggable(true);
layer.draw();
});
// add hover styling
anchor.on('mouseover', function() {
var layer = this.getLayer();
document.body.style.cursor = 'pointer';
this.setStrokeWidth(4);
layer.draw();
});
anchor.on('mouseout', function() {
var layer = this.getLayer();
document.body.style.cursor = 'default';
this.setStrokeWidth(2);
layer.draw();
});
group.add(anchor);
}
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
function initStage(images) {
var stage = new Kinetic.Stage({
container: 'container',
width: 1280,
height: 1024
});
var darthVaderGroup = new Kinetic.Group({
x: 270,
y: 100,
draggable: true
});
var yodaGroup = new Kinetic.Group({
x: 100,
y: 110,
draggable: true
});
var layer = new Kinetic.Layer();
/*
* go ahead and add the groups
* to the layer and the layer to the
* stage so that the groups have knowledge
* of its layer and stage
*/
layer.add(darthVaderGroup);
layer.add(yodaGroup);
stage.add(layer);
// darth vader
var darthVaderImg = new Kinetic.Image({
x: 0,
y: 0,
image: images.darthVader,
width: 200,
height: 138,
name: 'image'
});
darthVaderGroup.add(darthVaderImg);
addAnchor(darthVaderGroup, 0, 0, 'topLeft');
addAnchor(darthVaderGroup, 200, 0, 'topRight');
addAnchor(darthVaderGroup, 200, 138, 'bottomRight');
addAnchor(darthVaderGroup, 0, 138, 'bottomLeft');
darthVaderGroup.on('dragstart', function() {
this.moveToTop();
});
// yoda
var yodaImg = new Kinetic.Image({
x: 0,
y: 0,
image: images.yoda,
width: 93,
height: 104,
name: 'image'
});
yodaGroup.add(yodaImg);
addAnchor(yodaGroup, 0, 0, 'topLeft');
addAnchor(yodaGroup, 93, 0, 'topRight');
addAnchor(yodaGroup, 93, 104, 'bottomRight');
addAnchor(yodaGroup, 0, 104, 'bottomLeft');
yodaGroup.on('dragstart', function() {
this.moveToTop();
});
stage.draw();
}
var sources = {
darthVader: 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg',
yoda: 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg'
};
loadImages(sources, initStage);
</script>
</body>
</html>