I am new to Paper.js, I am creating a simple drawing tool using paper.js and download the canvas with the background image. I'm trying filesaver.js and blob however, I'm stuck downloading the canvas with a raster image as background.
Any help or suggestions would be highly appreciated.
I tried:
'''
HTML:
//button to download
<button href="#" class="btn btn-primary" id="save-canvas"
onclick="exportPNG()">Save</button>
//canvas
<canvas class="canvas" resize id="canvas"></canvas>
//image
<img src="image/image.jpg" id="image">
JS:
function exportPNG() {
var canvas = document.getElementById('canvas');
canvas.toBlob(function (blob) {
saveAs(blob, 'paper.png');
});
}
// Setup Paper
paper.setup(document.querySelector('canvas'));
//Image Layer
var imageLayer = new Layer({insert: false});
var image = new Raster('image');
image.position = view.center;
project.layers.push(imageLayer);
imageLayer.addChild(image);
imageLayer.activate();
'''
If I understand your request, you can do this quite simply.
There are some gotchas though like the fact that you need to wait for the image to be loaded and for the scene to be drawn before exporting the canvas content as image...
Here is a sketch demonstrating one way of doing it.
// Create a raster that will be used as background.
const raster = new Raster({
crossOrigin: 'anonymous',
source: 'http://assets.paperjs.org/images/marilyn.jpg',
position: view.center
});
// Wait for the raster to be loaded...
raster.onLoad = () => {
// ...and center it.
raster.position = view.center;
// Draw a circle over it.
new Path.Circle({
center: view.center,
radius: 50,
fillColor: 'blue'
});
// Make sure that the scene is drawn on the canvas.
view.update();
// Create a blob from the canvas...
view.element.toBlob((blob) => {
// ...and get it as a URL.
const url = URL.createObjectURL(blob);
// Open it in a new tab.
window.open(url, '_blank');
});
};
Related
I am able to get the image behind the drawing canvas but when i start drawing on the canvas the image dissapear
var $ = function(id){return document.getElementById(id)};
var canvas = this.__canvas = new fabric.Canvas('c', {
isDrawingMode: true
});
ctx = canvas.getContext("2d");
var background = new Image();
background.src = "images/pic01.jpg";
// Make sure the image is loaded first otherwise nothing will draw.
background.onload = function(){
ctx.drawImage(background,0,0);
}
That is not how you draw images using fabric.
Here is the correct way using fabric.Image.fromURL
var canvas = new fabric.Canvas('c', { isDrawingMode: true });
fabric.Image.fromURL('http://i.stack.imgur.com/UFBxY.png', function(myImg) {
canvas.add(myImg);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.4/fabric.min.js"></script>
<canvas id="c" width="300" height="300"></canvas>
When changing a selected canvas image to a larger image using setSrc(), mouse events are not recognized on areas of the new image that lie outside of the xy boundaries of the original smaller image.
Conversely, when changing a selected canvas image to a smaller image, mouse events are recognized on areas outside of the new image up to the xy boundaries of the original larger image.
In either case, once the new image does receive a mouse click, things return to normal where the entire image receives mouse events, no more, no less. Also, the controls appear in the correct locations but are not clickable as stated above.
Is there a way to correct this behavior so only the complete visible image can receive mouse events?
http://jsfiddle.net/kamakalama/0ng18cas/10/
HTML
<body>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<button id="butt">Change Image</button>
<canvas style="border:1px solid silver;" width=500 height=400 id="c"></canvas>
</body>
JavaScript
$(document).ready(function() {
var canvas = new fabric.Canvas('c',{backgroundColor:"#ffffff"});
var bigImg="http://nonsuch30.com/yachtcards/images/cardbox-closed.jpg"
var smallImg="http://nonsuch30.com/yachtcards/images/woodcrafter-thumb.jpg"
fabric.Image.fromURL(smallImg, function (img) {
canvas.add(img);
canvas.renderAll();
img.setTransformMatrix([ 1, 0, 0, 1, 0, 0])
canvas.setActiveObject(img)
})
$("#butt").click(function(){
img = canvas.getActiveObject()
if(img.getSrc()==smallImg){
img.setSrc(bigImg)
}else{
img.setSrc(smallImg)
}
setTimeout(function(){
canvas.renderAll()
canvas.setActiveObject(img)
}, 2000);
});
});
You're correct in using setCoords(), but put that and canvas.renderAll() in the setSrc() callback so you can remove the setTimeout() function call:
if (img.getSrc() == smallImg) {
img.setSrc(bigImg, function() {
canvas.renderAll();
img.setCoords();
});
} else {
img.setSrc(smallImg, function() {
canvas.renderAll();
img.setCoords();
});
}
Calling setCoords() on the new image corrects the problem.
Is it possible to move an image behind a mask without moving the mask itself ? I am looking for an operation which allow to move an image behind a mask and it should be accurate and smooth.
The best answer I am looking is to mask an Kinetic.Image object. Kinetic.Image is draggable and need to worry about it's movement. Please let me know if it's really possible to mask Kinetic.Image Object ?
A Demo: http://jsfiddle.net/m1erickson/u28MS/
Use a Kinetic.Shape to get access to the canvas context and then create a clipping region.
Create a new Kinetic.Shape
Define your non-rectangular path in the shape
Call clip() to restrict drawing to that path.
Draw the image into the clipping region.
Give the image x & y properties so that the image can be draw
Here's what that looks like in code:
// create a Kinetic.Shape which gives you access
// to a context to draw on
clippingShape = new Kinetic.Shape({
sceneFunc: function(context) {
// define your path here
// context.beginPath(); ...
// make your path a clipping region
context.clip();
// draw the image inside the clipping region
// img.x & img.y are offsets which can be used
// to "drag" the image around the clipping region
context.drawImage(img,img.x,img.y);
// KineticJS specific context method
context.fillStrokeShape(this);
},
stroke: 'black',
strokeWidth: 4,
listening:false
});
Listen for mouse events on the stage to cause the image to reposition when drawn in the Shape.
In mousedown: Save the mouse position and set a flag indicating the drag has begun.
In mousemove: Calc how much the mouse has moved and offset the image's x/y by that distance.
In mouseup: clear the dragging flag since the drag is over.
The mouse event handlers look like this:
var isdown=false;
stage.getContent().onmousedown=function(e){
var pos=stage.getPointerPosition();
img.lastX=parseInt(pos.x);
img.lastY=parseInt(pos.y);
isdown=true;
};
stage.getContent().onmouseup=function(e){
isdown=false;
};
stage.getContent().onmousemove=function(e){
if(!isdown){return;}
var pos=stage.getPointerPosition();
var mouseX=parseInt(pos.x);
var mouseY=parseInt(pos.y);
var dx=mouseX-img.lastX;
var dy=mouseY-img.lastY;
img.lastX=mouseX;
img.lastY=mouseY;
img.x+=dx;
img.y+=dy;
layer.draw();
};
[ Previous version of answer -- replaced with new answer above after questioners commments ]
This kind of clipping is traditionally done with a foreground image that contains a transparent "viewport" which lets the user see a portion of the background image beneath.
A Demo: http://jsfiddle.net/m1erickson/2f9yu/
Create a draggable background image on a bottom layer:
// create a background layer
var bottomLayer=new Kinetic.Layer();
stage.add(bottomLayer);
// put a draggable image on the background layer
var city=new Kinetic.Image({ image:bk,x:0,y:0,draggable:true,width:700,height:440, });
bottomLayer.add(city);
bottomLayer.draw();
Create a non-draggable foreground image on a top layer.
The top image has a transparent "viewport".
Important: the top layer does not listen for events, so dragging moves the bottom image, not the top image.
// create a top layer that does not respond to mouse events
// any mouse events will filter down to the background image
// this enables the background to be dragged even while behind the top image
var topLayer=new Kinetic.Layer({listening:false,});
stage.add(topLayer);
// create a top image with transparent pixels
// used as a viewport to see a portion of the bottom image
var mirror=new Kinetic.Image({ image:viewport,x:0,y:0 });
topLayer.add(mirror);
topLayer.draw();
Example code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Prototype</title>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.1.min.js"></script>
<style>
body{padding:20px;}
#container{
border:solid 1px #ccc;
margin-top: 10px;
width:350px;
height:300px;
}
</style>
<script>
$(function(){
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 300
});
var layer = new Kinetic.Layer();
stage.add(layer);
var bottomLayer=new Kinetic.Layer();
stage.add(bottomLayer);
var topLayer=new Kinetic.Layer({listening:false,});
stage.add(topLayer);
var loadedCount=0;
//
var bk=new Image();
bk.onload=start;
bk.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/desert1.jpg";
//
var viewport=new Image();
viewport.onload=start;
viewport.src="https://dl.dropboxusercontent.com/u/139992952/multple/car4.png";
function start(){
if(++loadedCount<2){return;}
var city=new Kinetic.Image({ image:bk,x:0,y:0,draggable:true,width:700,height:440, });
bottomLayer.add(city);
bottomLayer.draw();
var mirror=new Kinetic.Image({ image:viewport,x:0,y:0 });
topLayer.add(mirror);
topLayer.draw();
}
}); // end $(function(){});
</script>
</head>
<body>
<h4>Drag to move the background image in the mirror</h4>
<div id="container"></div>
</body>
</html>
I have this fiddle where I have text arranged in a circle, I would like now to animate it and rotate the text in a clockwise/counter clockwise motion.
Every animation demo I have seen uses a container as the starting point however all the examples i could find about manipulating text in a circular arrangement have all started with the element. I have tried 100's of variations trying to get this working but I am either missing something or it's not possible with the construction i have used thus far.
Here is the fiddle for the circular text I have so far:
http://jsfiddle.net/jamesburt/Sa2G8/
<canvas id="canvas1" width="500" height="500"></canvas>
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
Where as the animation examples start off with:
<div id="container"></div>
var stage = new Kinetic.Stage({container: 'container'});
I'm open to any ideas / rewrites needed as ultimately my goal is an animated text circle.
Also if this is easily accomplished in an alternative to KineticJS I'd be interested in trying that out.
Here is a demo I made using KineticJS: http://jsfiddle.net/Moonseeker/Xf7hp/
var stage = new Kinetic.Stage({
container: 'container',
width: 500,
height: 500
});
var layer = new Kinetic.Layer();
var myText = "My text in a circle. ";
var centerCoords = {x:250, y:250};
for(var i=0;i<myText.length;i++){
var rotation = i*360/myText.length;
var oneChar = new Kinetic.Text({
x: centerCoords.x,
y: centerCoords.y,
text: myText[i],
fontSize: 30,
fontFamily: 'Calibri',
fill: 'green',
offset: {x:0, y:100},
rotationDeg: rotation
});
layer.add(oneChar);
}
// add the layer to the stage
stage.add(layer);
var angularSpeed = Math.PI / 2;
var anim = new Kinetic.Animation(function(frame){
var angleDiff = frame.timeDiff * angularSpeed / 1000;
for(var i=0;i<layer.children.length;i++){
layer.children[i].rotate(angleDiff);
};
}, layer);
anim.start();
You can rotate at every direction or speed you wish, you can change the style of the circle.
You should be able to use layer.find('Text').each() instead of the for-loop for looping through the text to rotate but the KineticJS library at jsfiddle seems outdated as it doesn't know the find method.
One efficient way:
Render your text-around-a-circle on an offscreen canvas.
Save that offscreen canvas as an image using .toDataURL
Create a Kinetic.Image from that offscreen image.
You can then efficiently rotate/animate the Kinetic.Image as you need.
I am trying to create a simple rectangle with a .mp4 video as texture. As per three.js documentation(http://threejs.org/docs/#Reference/Textures/Texture) this should be straight forward.
When I am putting link of video, all I am getting is a black colored box with no texture on it. I have tested code by replacing video with a jpg image and it works fine. Can someone please explain me what I am doing wrong.
I have already seen the examples in which video is played by first linking it to a video element and then copy the frames on a canvas. I want to try the direct way as mentioned in the three.js documentation.
Think of video as a sequence of images. So to "play" this video on your 3D object - you'll have to pass every single frame of that sequence to your material and then update that material.
Good place to start is here: https://github.com/mrdoob/three.js/wiki/Updates
And here: http://stemkoski.github.io/Three.js/Video.html
Step 1:
Add a video to your HTML and "hide" it:
<video id="video" playsinline webkit-playsinline muted loop autoplay width="320" height="240" src="some-video.mp4" style="display: none;"></video>
Step 2:
//Get your video element:
const video = document.getElementById('video');
//Create your video texture:
const videoTexture = new THREE.VideoTexture(video);
const videoMaterial = new THREE.MeshBasicMaterial( {map: videoTexture, side: THREE.FrontSide, toneMapped: false} );
//Create screen
const screen = new THREE.PlaneGeometry(1, 1);
const videoScreen = new THREE.Mesh(screen, videoMaterial);
scene.add(videoScreen);
In addition to Haos' answer, I needed to set videoTexture.needsUpdate = true; and videoMaterial.needsUpdate = true;. Also I've played the video on onloadeddata.
//Get your video element:
const video = document.getElementById("video");
video.onloadeddata = function () {
video.play();
};
//Create your video texture:
const videoTexture = new THREE.VideoTexture(video);
videoTexture.needsUpdate = true;
const videoMaterial = new THREE.MeshBasicMaterial({
map: videoTexture,
side: THREE.FrontSide,
toneMapped: false,
});
videoMaterial.needsUpdate = true;
//Create screen
const screen = new THREE.PlaneGeometry(10, 10);
const videoScreen = new THREE.Mesh(screen, videoMaterial);
scene.add(videoScreen);