I have got some code for rotation of an image. But however, the center of rotation is not equal to the center of the image which is to be rotated.
I have set the center of an image but I don't know how to set the center of the rotation.
Following is the code :
// image code
var image = Titanium.UI.createImageView({
backgroundImage:'test.png',
width: 650,
height: 650,
center:{x:CenterX, y:CenterY},
align:'center'
});
//rotation code
image.addEventListener('touchstart', function(e){
var conv = e.source.convertPointToView({x: e.x, y:e.y}, win);
var newAngle = Math.atan2(conv.y - 500, 380 - conv.x)* -(180 / Math.PI);
diff = (newAngle - old);
});
image.addEventListener('touchmove', function(e){
var conv = e.source.convertPointToView({x: e.x, y:e.y}, win);
var newAngle = Math.atan2(conv.y - 500, 380 - conv.x)* -(180 / Math.PI);
current = (newAngle-diff);
var t = Ti.UI.create2DMatrix().rotate(current);
wheel.transform = t;
});
You have to set the animation / view anchorPoint, on iOS you have to set the Ti.UI.View's anchorPoint, on Android you have to set the Ti.UI.Animation's anchorPoint or in your case, since your using a Matrix, just set the anchorPoint of the Matrix on creation (for Android).
Here is what you do for iOS:
/* iOS */
var image = Titanium.UI.createImageView({
backgroundImage:'test.png',
width: 650,
height: 650,
anchorPoint: {0.5, 0.5}, // ANchor point is at the center
});
Or alternatively, on Android:
/* Android */
// Matrix, inside your event listener
image.addEventListener('touchmove', function(e){
var conv = e.source.convertPointToView({x: e.x, y:e.y}, win);
var newAngle = Math.atan2(conv.y - 500, 380 - conv.x)* -(180 / Math.PI);
current = (newAngle-diff);
// Note that I set the anchor point here.
var t = Ti.UI.create2DMatrix({anchorPoint: {0.5, 0.5}}).rotate(current);
wheel.transform = t;
});
Related
I have been asked, using Konvajs, to work out an animation that will rotate a circle as if spinning on its central x-axis. So imagine a coin spinning on a table. The intention is to reveal some text on the circle. At the start the circle is fully visible as if from behind so no text visible, then it flips to reveal the text.
I have this code that does a rotation like a spinning wheel.
Can anyone give me a tween / animation approach that would achieve the spinning coin effect?
// the tween has to be created after the node has been added to the layer
var tween = new Konva.Tween({
node: group,
duration: 4,
rotation: 360,
easing: Konva.Easings.BackEaseOut
}
});
tween.play();
After some research it looks like a 3D spin requires heavier lifting which may not be available or work well on mobile.
A good second-best appears to be using scaleX and animating from 0 > 1.
group.scaleX(0);
var tween = new Konva.Tween({
node: group,
duration: .25,
scaleX: 1,
easing: Konva.Easings.EaseOut
});
Here is an example of the second-best version using scaleX() effect. Because of the need to calculate scaleX() and control visibility of the text so as to make it appear that the disc is solid, I moved away from a tween and over to an animation().
// Set up the canvas / stage
var s1 = new Konva.Stage({container: 'container1', width: 300, height: 200});
// Add a layer for line
var layer = new Konva.Layer({draggable: false});
s1.add(layer);
// just a plain JS object to keep common variables in hand.
var cfg = { w: 300, h: 200, r: 80, txtSize: 520};
var group = new Konva.Group();
var circle = new Konva.Circle({x: cfg.w/2, y: cfg.h/2, radius: cfg.r, fill: 'DodgerBlue', stroke: 'DeepPink', strokeWidth: 5})
group.add(circle)
var textValue = new Konva.Text({
id: "t1",
x: cfg.w/2,
y: cfg.h/2,
text: '',
fill: 'DeepPink ',
fontSize: cfg.txtSize
});
group.add(textValue);
textValue.offset({x: textValue.getWidth()/2, y: textValue.getHeight()/2});
layer.add(group)
// to spin a group about a point, set the offset to that point, then set the x & y to that point to !
var pos = group.getClientRect();
RotatePoint(group, {x: pos.x + pos.width/2, y: pos.y + pos.height/2});
// Everything is ready so draw the canvas objects set up so far.
s1.draw()
$('#st').on('click', function(){
group.scaleX(1);
var txt = $('#theText').val();
setValue(txt);
})
// set the offset for rotation to the given location and re-position the shape
function RotatePoint(shape, pos){ // where pos = {x: xpos, y: yPos}
var initialPos = shape.getAbsolutePosition();
var moveBy = {x: pos.x - initialPos.x, y: pos.y - initialPos.y};
// offset is relative to initial x,y of shape, so deduct x,y.
shape.offsetX(moveBy.x);
shape.offsetY(moveBy.y);
shape.x(initialPos.x + moveBy.x);
shape.y(initialPos.y + moveBy.y);
}
var setValue = function(newText){
// work out scaling to make text fit into the circle
var txt = this.layer.find('#t1')[0];
txt.text(newText).scale({x:1, y: 1})
var txtSize = txt.getClientRect();
var maxW = (cfg.r); // max allowed width of text
var txtScaleW = (txtSize.width > maxW ? ( maxW / txtSize.width) : 1);
var maxH = cfg.r; // max allowed height of text
var txtScaleH = (txtSize.height > maxH ? ( maxH / txtSize.height) : 1);
// finally decide which is the worst case and use that scaling
var txtScale = ( txtScaleW > txtScaleH ? txtScaleH : txtScaleW);
txt.scale({x: txtScale, y: txtScale});
txt.offset({x: txt.getWidth()/2, y: txt.getHeight()/2});
layer.draw()
}
// set initial text & spin !
setValue('BBB');
var anim, pos = 0, frameCnt = 0
if (anim) {anim.stop(); }
anim = new Konva.Animation(function(frame) {
frameCnt = frameCnt + 1;
if (frameCnt % 2 === 0){
pos = pos + .2
var scaleX = Math.sin(pos)
textValue.visible(scaleX < 0 ? false : true);
group.scaleX(scaleX);
if (pos % 360 === 0){ console.log('spin') }
}
}, layer);
anim.start();
div
{
float: left;
margin: 0 5px;
}
p
{
margin: 0 5px 5px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.min.js"></script>
<div id='container1' style="width: 300px, height: 200px;"></div>
<div>
<p> <input type='text' id='theText' value='BBB' /> <button id='st'>Change text</button> </p>
</div>
I need to rotate the image 180 degrees, but when I add in the rotate code, the image becomes a square.
var moulding_matte_canvas_height = [],
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d"),
width = 5.171363636363637,
opening_i = 0,
rotate = 180 * Math.PI / 180,
i = 2;
moulding_matte_canvas_height[0] = 225;
canvas.width = 285;
canvas.height = 335;
img = new Image();
img.src = 'http://www.asa.tframes.org:1881/system/components/compimg/80ac89fad42cf14561b641df241bf406/pattern';
function moulding_get_segment_width(img_width, opening_i)
{
return 175;
}
//
$(img).on("load", function() {
ctx.save();
ctx.translate(width, moulding_matte_canvas_height[opening_i]);
//ctx.rotate(rotate);
//ctx.rotate(Math.PI);
ctx.drawImage(img, 0, 0, moulding_get_segment_width(img.width, i), width);
ctx.restore();
//
});
$("#mattes").append(canvas); /*appending the canvas*/
http://fiddle.jshell.net/onpa628e/1/
(Whitespace is intentional due to other elements that will be drawn and the red next to the image is just for clarity)
Without rotate:
With rotate:
Expected:
Your image looks like a square, because it's only showing a tiny portion on the canvas. This happens because translate is only moving ~ 5px (your "width" variable) on the x axis.
Here's your example modified with a higher x value.
Once a transformation of coordinates has taken place during a the rendering of a computer graphics scene, how do you map inputs on the rendered scene back to the original actor(s) coordinate systems?
Using this JSFiddle https://jsfiddle.net/bbz5s183/3/ as a starting point, implement the canvas click event handler so that.
It can identify if a star was clicked.
It will work consistently no matter how the canvas is resized.
JSFIDDLE SCRIPT CONTENT BELOW
var draggable = document.getElementById('draggable')
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
// Draw a star in a 1 x 1 coordinate plane.
function star(color) {
context.beginPath();
context.moveTo(0.5, 0);
context.lineTo(0.15, 1.0);
context.lineTo(1.0, 0.4);
context.lineTo(0, 0.4);
context.lineTo(0.85, 1.0);
context.closePath();
context.fillStyle = color;
context.fill();
}
// Draw a scene of stars in a coordinate plane defined by the canvas.
// This is initially 300 x 300, but can be resized to anything by dragging the gray border.
function render() {
context.setTransform(1, 0, 0, 1, 0, 0);
context.translate(canvas.width / 2, canvas.height / 2);
context.scale(canvas.width / 5, canvas.height / 5);
star('red');
context.setTransform(1, 0, 0, 1, 0, 0);
context.translate(canvas.width / 4, canvas.height / 4);
context.scale(canvas.width / 5, canvas.height / 5);
star('yellow');
}
// Pop an alert indicating which star (if any) was clicked on.
// NOTE: The logic MUST work consistently no matter how the canvas is resized.
canvas.addEventListener('click', function (event) {
event.stopImmediatePropagation();
// HELP ME !!!
// HELP ME !!!
// HELP ME !!!
// HELP ME !!!
});
// IGNORE: It allows the canvas to resized by dragging on it.
draggable.addEventListener('mousedown', function handleMouseDown(mousedown) {
document.addEventListener('mouseup', function handleMouseUp(mouseup) {
var currWidth = Number(canvas.getAttribute('width'));
var deltaWidth = mouseup.clientX - mousedown.clientX;
var currHeight = Number(canvas.getAttribute('height'));
var deltaHeight = mouseup.clientY - mousedown.clientY;
canvas.setAttribute('width', currWidth + deltaWidth);
canvas.setAttribute('height', currHeight + deltaHeight);
document.removeEventListener('mouseup', handleMouseUp);
render();
});
});
render();
Answered my own question: https://jsfiddle.net/bbz5s183/4/
JAVASCRIPT FOLLOWS
// Draw a scene of stars in a coordinate plane defined by the canvas.
// This is initially 300 x 300, but can be resized to anything by dragging the gray border.
function render() {
bounds = [];
/* RENDER RED ACTOR - BOUNDING BOX */
var red = {
name: 'red',
// Translate to 25% right, 25% down on canvas.
x: 0.25 * canvas.width,
y: 0.25 * canvas.height,
// Scale to fill 20% of canvas.
width: 0.2 * canvas.width,
height: 0.2 * canvas.height
};
context.setTransform(1, 0, 0, 1, 0, 0);
box('red', red);
bounds.push(red);
/* RENDER RED ACTOR - MODEL */
context.setTransform(1, 0, 0, 1, 0, 0);
context.translate(red.x, red.y);
context.scale(red.width, red.height);
star('red');
/* RENDER YELLOW ACTOR - BOUNDING BOX */
var yellow = {
name: 'yellow',
// Translate to 50% right, 50% down on canvas.
x: 0.50 * canvas.width,
y: 0.50 * canvas.height,
// Scale to fill 20% of canvas.
width: 0.2 * canvas.width,
height: 0.2 * canvas.height
};
context.setTransform(1, 0, 0, 1, 0, 0);
box('yellow', yellow);
bounds.push(yellow);
/* RENDER YELLOW ACTOR - MODEL */
context.setTransform(1, 0, 0, 1, 0, 0);
context.translate(yellow.x, yellow.y);
context.scale(yellow.width, yellow.height);
star('yellow');
}
// Pop an alert indicating which star (if any) was clicked on.
// NOTE: The logic MUST work consistently no matter how the canvas is resized.
canvas.addEventListener('click', function (event) {
var x = event.pageX - event.target.offsetLeft;
var y = event.pageY - event.target.offsetTop;
for (var i = 0; i < bounds.length; i++) {
if (boxIntersection(bounds[i], x, y)) {
alert(bounds[i].name);
};
}
event.stopImmediatePropagation();
return false;
});
i am trying to add add text (water mark) in canvas generated image
here is my code.
html2canvas($("#frame"), {
onrendered: function (canvas) {
$("#outputImage").html(canvas);
}
what i should add in this code to add watermark mark in generated image
Inside the handler do the following:
html2canvas($("#frame"), {
onrendered: function (canvas) {
var ctx = canvas.getContext("2d"); // get 2D context of canvas
ctx.textBaseline = "top"; // start with drawing text from top
ctx.font = "20px sans-serif"; // set a font and size
ctx.fillStyle = "red"; // set a color for the text
ctx.fillText("WATERMARK", 20, 20); // draw the text at some position (x, y)
$("#outputImage").html(canvas);
}
}
There is also alignment modes using:
ctx.textAlign = "center"; // default: "start" = left-to-right/right-to-left
// depending on language, override with "left" or "right"
Check out Canvas.globalCompositeOperation. If you set it to the string 'lighter', it will lighten the pixels drawn by the next drawing command (such as fillText()). Here's a sample
<canvas id='canvas'></canvas>
<script>
var img = new Image();
img.src = 'http://www.javascripture.com/pic.jpg';
img.onload = function() {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, canvas.width, canvas.height);
// Draw the Watermark
context.font = '48px sans-serif';
context.globalCompositeOperation = 'lighter';
context.fillStyle = '#444';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText('watermark', canvas.width / 2, canvas.height / 2);
};
</script>
when you drag images you can control their moving with dragBoundFunc. Is there something similar when you scaling? I want to set an image area in "part of a stage" and when i setScale for this image and make it bigger, i don't want to see parts which is bigger than image area i've set before. is it possible? Here is my fiddle..
var stage = new Kinetic.Stage({
container : 'canvas',
width : 620,
height : 236
});
var layer = new Kinetic.Layer();
var leftImage = new Image();
leftImage.src = "http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg";
var leftImg = new Kinetic.Image({
x : stage.getWidth() - 480,
y : stage.getHeight() - 126,
image : leftImage,
width : 190,
height : 124,
offset : [95, 62],
draggable : true,
dragBoundFunc: function(pos) {
var x=stage.getWidth() - 480;
var y=stage.getHeight() - 126;
var radius = 50;
var scale = radius/ Math.sqrt(Math.pow(pos.x - x, 2) + Math.pow(pos.y - y, 2));
if(scale < 1)
return {
y: Math.round((pos.y - y) * scale + y),
x: Math.round((pos.x - x) * scale + x)
};
else
return pos;
}
});
var rectLeft = new Kinetic.Rect({
x : 38,
y : 20,
width : 232,
height : 184,
stroke:'red',
listening:false
});
var rectRight = new Kinetic.Rect({
x : 350,
y : 20,
width : 232,
height : 184,
stroke:'green',
listening:false
});
layer.add(leftImg);
layer.add(rectLeft);
layer.add(rectRight);
stage.add(layer);
document.getElementById('larger').addEventListener('click', function() {
leftImg.setScale(leftImg.getScale().x + 1.5);
layer.draw();
}, false);
Sure.
You can clip your scaled image into a fixed area of the stage by wrapping your image in a group and setting the clip property of that group to your fixed area.
If you set the clip property on a group like this:
var group=new Kinetic.Group({
x:100,
y:100,
width: originalImageWidth,
height: originalImageHeight,
clip: [0,0,originalImageWidth,originalImageHeight]
});
Then the image you put in the group will be clipped to stage #100,100 and size == original image size.
If you later scale the image larger, the clipping area will act as a "viewport" into part of the larger image.