I am creating an enyo Control based on a canvas. It should capture mouse or finger events, and draw them onto it. However when I draw onto that canvas it draws only into a smaller part of it.
Look at that jsfiddle as it contains all relevant code.
enyo.kind({
name: "SignatureControl",
kind: "enyo.Canvas",
recording: false,
points: [],
handlers: {
ondown: "startRecord",
onmove: "record",
onup: "stopRecord"
},
startRecord: function(inSender, inEvent) {
this.recording = true;
if(node = this.hasNode()) {
this.points.push({
x: inEvent.clientX - node.offsetLeft,
y: inEvent.clientY - node.offsetTop,
d: false,
p: 1
});
}
this.update();
},
stopRecord: function() {
this.recording = false;
},
record: function(inSender, inEvent) {
if( this.recording ) {
if(node = this.hasNode()) {
this.points.push({
x: inEvent.clientX - node.offsetLeft,
y: inEvent.clientY - node.offsetTop,
d: true,
p: 1
});
}
this.update();
}
},
update: function() {
var canvas = this.hasNode();
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
this.log(ctx.canvas.width);
ctx.lineJoin = "round";
ctx.lineWidth = 1;
var i = this.points.length - 1;
ctx.strokeStyle = "rgba(0,0,0," + this.points[i].p + ")";
ctx.beginPath();
if(this.points[i].d && i){
ctx.moveTo(this.points[i-1].x, this.points[i-1].y);
}else{
ctx.moveTo(this.points[i].x-1, this.points[i].y);
}
ctx.lineTo(this.points[i].x, this.points[i].y);
ctx.closePath();
ctx.stroke();
}
}
});
You can only use the height/width attributes on the canvas, not size it via CSS. Check out this updated fiddle http://jsfiddle.net/AFqvD/4/
The relevant portion is:
{kind: "SignatureControl", attributes: {height: 150, width: 300}}
Related
I am trying to display text within a mesh that gets created using a component. Here is the component that creates a rectangular shape with rounded edges. I got it from github link for aframe-rounded
Here is the code for this component
AFRAME.registerComponent('rounded', {
schema: {
enabled: {default: true},
width: {type: 'number', default: 1},
height: {type: 'number', default: 1},
radius: {type: 'number', default: 0.3},
topLeftRadius: {type: 'number', default: -1},
topRightRadius: {type: 'number', default: -1},
bottomLeftRadius: {type: 'number', default: -1},
bottomRightRadius: {type: 'number', default: -1},
color: {type: 'color', default: "#F0F0F0"},
opacity: {type: 'number', default: 1}
},
init: function () {
this.rounded = new THREE.Mesh( this.draw(), new THREE.MeshPhongMaterial( { color: new THREE.Color(this.data.color), side: THREE.DoubleSide } ) );
this.updateOpacity();
this.el.setObject3D('mesh', this.rounded)
},
update: function () {
if (this.data.enabled) {
if (this.rounded) {
this.rounded.visible = true;
this.rounded.geometry = this.draw();
this.rounded.material.color = new THREE.Color(this.data.color);
this.updateOpacity();
}
} else {
this.rounded.visible = false;
}
},
updateOpacity: function() {
if (this.data.opacity < 0) { this.data.opacity = 0; }
if (this.data.opacity > 1) { this.data.opacity = 1; }
if (this.data.opacity < 1) {
this.rounded.material.transparent = true;
} else {
this.rounded.material.transparent = false;
}
this.rounded.material.opacity = this.data.opacity;
},
tick: function () {},
remove: function () {
if (!this.rounded) { return; }
this.el.object3D.remove( this.rounded );
this.rounded = null;
},
draw: function() {
var roundedRectShape = new THREE.Shape();
function roundedRect( ctx, x, y, width, height, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius ) {
if (!topLeftRadius) { topLeftRadius = 0.00001; }
if (!topRightRadius) { topRightRadius = 0.00001; }
if (!bottomLeftRadius) { bottomLeftRadius = 0.00001; }
if (!bottomRightRadius) { bottomRightRadius = 0.00001; }
ctx.moveTo( x, y + topLeftRadius );
ctx.lineTo( x, y + height - topLeftRadius );
ctx.quadraticCurveTo( x, y + height, x + topLeftRadius, y + height );
ctx.lineTo( x + width - topRightRadius, y + height );
ctx.quadraticCurveTo( x + width, y + height, x + width, y + height - topRightRadius );
ctx.lineTo( x + width, y + bottomRightRadius );
ctx.quadraticCurveTo( x + width, y, x + width - bottomRightRadius, y );
ctx.lineTo( x + bottomLeftRadius, y );
ctx.quadraticCurveTo( x, y, x, y + bottomLeftRadius );
}
var corners = [this.data.radius, this.data.radius, this.data.radius, this.data.radius];
if (this.data.topLeftRadius != -1) { corners[0] = this.data.topLeftRadius; }
if (this.data.topRightRadius != -1) { corners[1] = this.data.topRightRadius; }
if (this.data.bottomLeftRadius != -1) { corners[2] = this.data.bottomLeftRadius; }
if (this.data.bottomRightRadius != -1) { corners[3] = this.data.bottomRightRadius; }
roundedRect( roundedRectShape, 0, 0, this.data.width, this.data.height, corners[0], corners[1], corners[2], corners[3] );
return new THREE.ShapeBufferGeometry( roundedRectShape );
},
pause: function () {},
play: function () {}
});
In order to display the text, here is how I am using it in my code
<a-entity rounded="width: 3; height: 1; color: #ff00ed; opacity: 0.4" text="align: left; baseline: bottom; anchor: left; wrapCount: 35; value: Animating color; width: 2.5" position="2 1 -3">
However, the text always shows up at the bottom or outside the mesh geometry. I have played around with values for baseline, anchor, wrapCount etc..but it never shows the text at the top of the mesh.
Here is the fiddle that shows the problem jsfiddle
Could someone please help?
Changing the value of x and y to set the position of the rounded mesh solved this issue. Specifically this piece of code
roundedRect( roundedRectShape, 0, -1, this.data.width, this.data.height, corners[0], corners[1], corners[2], corners[3] );
I have found a way to be able to resize and squeeze my text on normal HTML5 Canvas with the following code:
$("input").on("input", function() {
ctx.clearRect(0, 0, 400, 400);
var width = ctx.measureText(text).width;
if(width <= 100) {
ctx.fillText(text, 0, 100);
} else {
ctx.save();
ctx.scale(100 / width, 1);
ctx.fillText(text, 0, 100);
ctx.restore();
}
});
I want to do the same thing but with using the FabricJS. Is there a way that I can do it?
Yeah sure, just check the width of the text element after you've created it and set the ScaleX property if needed before adding it to the canvas:
var canvas = new fabric.Canvas('c');
var t;
canvas.renderAll();
$("input").on("input", function () {
if (t) {
canvas.remove(t);
}
var textToDraw = $(this).val();
t = new fabric.Text(textToDraw, {
left: 0,
top: 0
});
if (t.width > c.width) {
console.log('scale -> ' + (c.width / t.width));
t.setScaleX(c.width / t.width);
}
canvas.add(t);
});
If you wish to do this on object resize, use the modified event handler like so:
function scaleText() {
console.log("scale=" + t.getScaleX() + " w=" + t.getWidth());
if ((t.width * t.getScaleX()) > c.width) {
t.setScaleX(c.width / t.width);
}
}
$("input").on("input", function () {
if (t) {
canvas.remove(t);
}
var textToDraw = $(this).val();
t = new fabric.Text(textToDraw, {
left: 0,
top: 0
});
scaleText();
t.on("modified", function (options) {
scaleText();
});
canvas.add(t);
});
I have a KineticJS shape that draws a bezier curve that is wider on one end. It draws correctly, but I can't yet detect a 'mouseover' event on it. I have created a small JSFiddle demo of the anomaly, at:
http://jsfiddle.net/VikR0001/nZYxL/6/
How can I detect 'mouseover' events on this shape?
Thanks very much in advance to all for any info!
var mainLayer;
//bezier curve code:
//http://stackoverflow.com/questions/8325680/how-to-draw-a-bezier-curve-with-variable-thickness-on-an-html-canvas
//draw a bezier curve that gets larger as it flows
//adapted for use with KineticJS
function drawBezierCurve() {
var centerLeft = new Object();
centerLeft.x = 100;
centerLeft.y = 400;
var centerRight = new Object();
centerRight.x = 400;
centerRight.y = 100;
var thicknessLeft = 1;
var thicknessRight = 50;
var color = "#000";
var context = mainLayer.getContext();
var leftUpper = {
x: centerLeft.x,
y: centerLeft.y - thicknessLeft / 2
};
var leftLower = {
x: centerLeft.x,
y: leftUpper.y + thicknessLeft
};
var rightUpper = {
x: centerRight.x,
y: centerRight.y - thicknessRight / 2
};
var rightLower = {
x: centerRight.x,
y: rightUpper.y + thicknessRight
};
var center = (centerRight.x + centerLeft.x) / 2;
var cp1Upper = {
x: center,
y: leftUpper.y
};
var cp2Upper = {
x: center,
y: rightUpper.y
};
var cp1Lower = {
x: center,
y: rightLower.y
};
var cp2Lower = {
x: center,
y: leftLower.y
};
var bezierCurve = new Kinetic.Shape({
drawFunc: function (canvas) {
var context = mainLayer.getContext();
context.fillStyle = color;
context.beginPath();
context.moveTo(leftUpper.x, leftUpper.y);
context.bezierCurveTo(cp1Upper.x, cp1Upper.y, cp2Upper.x, cp2Upper.y, rightUpper.x, rightUpper.y);
context.lineTo(rightLower.x, rightLower.y);
context.bezierCurveTo(cp1Lower.x, cp1Lower.y, cp2Lower.x, cp2Lower.y, leftLower.x, leftLower.y);
context.lineTo(leftUpper.x, leftUpper.y);
context.fill();
canvas.stroke(this);
},
fill: color,
stroke: color,
strokeWidth: 1
});
bezierCurve.on('mouseover', function (evt) {
document.body.style.cursor = "pointer";
$("#debug").html("MOUSEOVER DETECTED."); //<==NEVER CALLED
});
bezierCurve.on('mouseout', function (evt) {
document.body.style.cursor = "default";
$("#debug").html("MOUSEOUT DETECTED."); //NEVER CALLED
});
bezierCurve.setAttrs({
'leftUpper': leftUpper,
'leftLower': leftLower,
'rightUpper': rightUpper,
'rightLower': rightLower,
'cp1Upper': cp1Upper,
'cp2Upper': cp2Upper,
'cp1Lower': cp1Lower,
'cp2Lower': cp2Lower
});
mainLayer.add(bezierCurve);
mainLayer.draw();
$("#debug").html("bezier curve has been drawn onscreen.");
}
$(document).ready(function () {
var stage = new Kinetic.Stage({
container: 'canvasContainer',
width: 500,
height: 500
});
mainLayer = new Kinetic.Layer('main');
stage.add(mainLayer);
mainLayer.draw();
drawBezierCurve();
});
Can you define it as an SVG element, and just give that an onmouseover?
Fixed it! Changes are shown at the jsFiddle link in the original post.
//FIXED!
//OLD VERSION: DOES NOT WORK
// var bezierCurve = new Kinetic.Shape({
// drawFunc: function (canvas) {
// var context = mainLayer.getContext();
// context.fillStyle = color;
// context.beginPath();
// context.moveTo(leftUpper.x, leftUpper.y);
// context.bezierCurveTo(cp1Upper.x, cp1Upper.y, cp2Upper.x, cp2Upper.y, rightUpper.x, rightUpper.y);
// context.lineTo(rightLower.x, rightLower.y);
// context.bezierCurveTo(cp1Lower.x, cp1Lower.y, cp2Lower.x, cp2Lower.y, leftLower.x, leftLower.y);
// context.lineTo(leftUpper.x, leftUpper.y);
// context.closePath();
// context.fill();
// canvas.stroke(this);
// },
// fill: color,
// stroke: color,
// strokeWidth: 1
// });
//NEW VERSION: WORKS!
var bezierCurve = new Kinetic.Shape({
drawFunc: function (canvas) {
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(leftUpper.x, leftUpper.y);
context.bezierCurveTo(cp1Upper.x,cp1Upper.y, cp2Upper.x,cp2Upper.y, rightUpper.x,rightUpper.y);
context.lineTo(rightLower.x, rightLower.y);
context.bezierCurveTo(cp1Lower.x,cp1Lower.y, cp2Lower.x,cp2Lower.y, leftLower.x,leftLower.y);
context.lineTo(leftUpper.x, leftUpper.y);
context.fill();
canvas.stroke(this);
},
fill: color,
stroke: color,
strokeWidth: 3
});
See here for example:
http://jsfiddle.net/tigz_uk/B8UDq/45/embedded/result/
Fiddle code:
http://jsfiddle.net/tigz_uk/B8UDq/45/
Most Relevant snippet:
function whenAreaSelected(stage, layer, image) {
var rect, down = false;
var eventObj = layer;
eventObj.off("mousedown");
eventObj.off("mousemove");
eventObj.off("mouseup");
eventObj.on("mousedown", function (e) {
console.log("Mousedown...");
if (rect) {
rect.remove();
}
var relativePos = getRelativePos ( stage, layer);
down = true;
var r = Math.round(Math.random() * 255),
g = Math.round(Math.random() * 255),
b = Math.round(Math.random() * 255);
rect = new Kinetic.Rect({
x: relativePos.x,
y: relativePos.y,
width: 11,
height: 1,
fill: 'rgb(' + r + ',' + g + ',' + b + ')',
stroke: 'black',
strokeWidth: 4,
opacity: 0.3
});
layer.add(rect);
});
eventObj.on("mousemove", function (e) {
if (!down) return;
var relativePos = getRelativePos ( stage, layer );
var p = rect.attrs;
rect.setWidth(relativePos.x - p.x);
rect.setHeight(relativePos.y - p.y);
layer.draw();
});
eventObj.on("mouseup", function (e) {
console.log("Mouse Up...");
down = false;
var p = rect.attrs;
var s = layer.getScale();
console.log("Rect x: " + p.x + " y: " + p.y + " width: " + p.width + " height: " + p.height + " sx: " + s.x + " sy: " + s.y);
});
}
var stageWidth = 1024;
var stageHeight = 700;
var imageWidth = 1299;
var imageHeight = 1064;
var initialScale = calcScale(imageWidth, imageHeight, stageWidth, stageHeight);
var stage = new Kinetic.Stage({
container: "canvas",
width: stageWidth,
height: stageHeight
});
var layer = new Kinetic.Layer();
var imageObj = new Image();
imageObj.onload = function () {
var diagram = new Kinetic.Image({
x: -500,
y: -500,
image: imageObj,
width: imageWidth,
height: imageHeight
});
layer.add(diagram);
layer.setScale(initialScale);
whenAreaSelected(stage, layer, diagram);
layer.draw();
}
var zoom = function (e) {
var zoomAmount = e.wheelDeltaY * 0.001;
layer.setScale(layer.getScale().x + zoomAmount)
layer.draw();
}
document.addEventListener("mousewheel", zoom, false);
stage.add(layer);
imageObj.src = 'https://dl.dropbox.com/u/746967/Serenity/MARAYA%20GA.png';
It seems to me as though the mouseup event is intermittent at best.
Any idea what's going on here? It also seems to be worse when the Image is offset rather than displayed at 0,0. And I think it relates to the scaling of the layer as it all works okay at scale 1.
Is this a kinetic bug?
Try using layer.drawScene() instead of layer.draw() in your mousemove handler
eventObj.on("mousemove", function (e) {
if (!down) return;
var relativePos = getRelativePos ( stage, layer );
var p = rect.attrs;
rect.setWidth(relativePos.x - p.x);
rect.setHeight(relativePos.y - p.y);
// try drawScene() instead of draw()
layer.drawScene();
});
[Edited based on info forwarded by from user814628 here: Binding MouseMove event causes inconsistency with mouse release event being fired
Check out my html5 based clipping constraint on
http://shedlimited.debrucellc.com/test3/canvaskinclip.html
(messing with jsfiddle on http://jsfiddle.net/aqaP7/4/)
So, in html5 I can easily draw a shaped boundary like the following:
context.beginPath();
context.moveTo(5, 5);
context.lineTo(34, 202);
context.lineTo(2, 405);
context.lineTo(212, 385);
context.lineTo(425, 405);
context.lineTo(400, 202);
context.lineTo(415, 10);
context.lineTo(212, 25);
context.clip();
In kinetic.js though, all I see for clipping options is: height, width, and x, y,
I came across the following : Mask/Clip an Image using a Polygon in KineticJS, but the inner/fill image can't be set to draggable
any help please!
In the new kineticJS versions, a lot of the work is done in the background for you.
Take a look at this tutorial:
This fiddle gets you pretty close, here's the code:
<body>
<div id="container"></div>
<script src="http://www.html5canvastutorials.com/libraries/kinetic-v4.3.0-beta2.js"></script>
<script>
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
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 draw(images) {
var stage = new Kinetic.Stage({
container: 'container',
width: 600,
height: 700
});
var layer = new Kinetic.Layer();
var patternPentagon = new Kinetic.RegularPolygon({
x: 220,
y: stage.getHeight() / 4,
sides: 5,
radius: 70,
fillPatternImage: images.yoda,
fillPatternOffset: [-220, 70],
stroke: 'black',
strokeWidth: 4,
draggable: true
});
patternPentagon.on('dragmove', function() {
//this.setFillPatternImage(images.yoda);
//this.setFillPatternOffset(-100, 70);
var userPos = stage.getUserPosition();
this.setFillPatternOffset(-userPos.x,-userPos.y);
layer.draw();
this.setX(220);
this.setY(stage.getHeight() / 4);
});
layer.add(patternPentagon);
stage.add(layer);
}
var sources = {
darthVader: 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg',
yoda: 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg'
};
loadImages(sources, function(images) {
draw(images);
});
</script>
</body>
There is a more complex/accurate way of doing this without making it a background pattern, like with grouping objects together