I'm trying to get the mouse coordinates during a jQuery animation, I'm doing this because I'm working on an interactive plug-in which moves the background image inside a div from covercss property to 100% of it's scale when the user go over the element.
I'm near to completing the plug-in but the animation is buggy because it work on the last position of the mouse fired by mousemove event of jQuery.
Does exists some way to avoid the problem?
This is my situation:
$(settings.selector).hover(function (e) {
$(this).bind('mousemove', setFollowMouse);
}, function () {
$(this).unbind('mousemove', setFollowMouse);
zoomOut();
});
var setFollowMouse = function (e) {
var o = {offsetLeft:this.offsetLeft, offsetTop:this.offsetTop};
if (!settings.bg.zooming_in && settings.bg.current_scale != 100) {
settings.bg.zooming_in = true;
zoomIn(e, o);
} else {
followMouse(e, o);
}
}
var zoomIn = function (e, o) {
$({scale:settings.bg.min_perc}).animate ({
scale:100
},{
easing:settings.zoom_in.easing,
duration:settings.zoom_in.duration,
step:function () {
settings.bg.current_scale = this.scale;
followMouse(e, o);
},
complete:function () {
settings.bg.current_scale = 100;
settings.bg.zooming_in = false;
followMouse(e, o);
}
});
}
var followMouse = function (e, o) {
var m_x = e.pageX - o.offsetLeft;
var m_y = e.pageY - o.offsetTop;
settings.bg.perc_pos_x = ((m_x * 100) / (a_w - bg_w)) + '%';
settings.bg.perc_pos_y = ((m_y * 100) / (a_h - bg_h)) + '%';
var bg_w = getScalePercent(settings.bg.width, settings.bg.current_scale);
var a_w = settings.container.width;
var bg_h = getScalePercent(settings.bg.height, settings.bg.current_scale);
var a_h = settings.container.height;
var bpx = - (bg_w - a_w) * m_x / a_w;
var bpy = - (bg_h - a_h) * m_y / a_h;
$(settings.selector).css({
backgroundPosition:bpx + 'px ' + bpy + 'px'
,backgroundSize:bg_w + 'px ' + bg_h + 'px'
});
}
As you see, I use animation to calculate the progressive scaling of the background-image, and trying to calculating it with the follow mouse method, but if I sto moving the mouse, the animation works with the last mousemove event.pageX and Y mouse position.
I've done this because I have problems with make animation method fluid if I trying to rewrite it continuously by with the mouse.
Should I follow some different way to avoid the bug?
forgive my dodgy math; but this should help!
<html>
<head>
<script type="text/javascript" charset="utf-8">
window.onload = function () {
var img = new Image();
img.src = 'http://wallpapers.free-review.net/wallpapers/23/Autumn_Theme_-_Windows_7_Backgrounds.jpg';
var canvas = document.getElementById("canvas1");
canvas.width = img.width;
canvas.height = img.height;
canvas.addEventListener('mousemove', onMouseMove, false);
var ctx = canvas.getContext("2d");
var scale = 0.9;
var scaledwidth = canvas.width * scale;
var scaledheight = canvas.height * scale;
var scaledcenterX = scaledwidth /2;
var scaledcenterY = scaledheight /2;
var animloop = setInterval(function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, scaledwidth, scaledheight, canvas.width - scaledcenterX, canvas.height - scaledcenterY, 0, 0, canvas.width, canvas.height);
}, .01);
function onMouseMove(e) {
mouseX = e.clientX - canvas.offsetLeft;
mouseY = e.clientY - canvas.offsetTop;
scale = mouseX/1000;
scaledwidth = canvas.width * scale;
scaledheight = canvas.height * scale;
}
};
</script>
<style>
body {
background: #001;
background-size: cover;
overflow: hidden;
}
#canvas1 {
position: absolute;
top: 0;
left: 0;
padding: 0;
margin: 0;
height: 100% auto;
}
</style>
</head>
<body>
<canvas id="canvas1"></canvas>
</body>
</html>
I've just solved the problem with this simple edit to my code:
var setFollowMouse = function (e) {
settings.mouse.x = e.pageX - this.offsetLeft;
settings.mouse.y = e.pageY - this.offsetTop;
if (!settings.bg.zooming_in && settings.bg.current_scale != 100) {
settings.bg.zooming_in = true;
zoomIn();
} else {
followMouse();
}
}
the old one:
var setFollowMouse = function (e) {
var o = {offsetLeft:this.offsetLeft, offsetTop:this.offsetTop};
if (!settings.bg.zooming_in && settings.bg.current_scale != 100) {
settings.bg.zooming_in = true;
zoomIn(e, o);
} else {
followMouse(e, o);
}
}
this has removed the buggy behavior.
Related
I am trying to fill a triangle shape on a HTML5 canvas drawn by dragging the mouse.
I have similar effect for circles, rectangles working.
The code showing both the working drawCircle and not working drawTriangle functions is below. The outline of the triangle gets drawn but it is not filled. I have tried loving the context.stroke line to various places in the sequence of there is no effect.
<style>
#divContainer {
width: 100%;
height: 80%;
background: #ddd;
}
#divContentArea {
left: 0px;
top: 0px;
right: 0px;
bottom: 0px;
}
.canvas {
cursor: crosshair;
position:relative;
left:0px;
top:0px;
}
</style>
<div>
Click the button to select the shape type then click and drag mouse on the canvas below.
<BR>
<button type="button" onClick='shapetype="circle";'>Draw Circle</button>
<button type="button" onClick='shapetype="triangle";'>Draw Triangle</button>
<BR>
</div>
<div id="divContainer">
<div id="divContentArea">
<canvas id="canvas" class='canvas'>
Sorry, your browser does not support a canvas object.
</canvas>
</div>
</div>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var canrect = canvas.getBoundingClientRect();
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var lastPoint;
var startPoint;
var isDrawing = false;
var shapetype = 'triangle';
canvas.onmousedown = function(e) {
isDrawing = true;
if ( shapetype == 'circle' ) {
canvas.removeEventListener("mousemove", drawTriangle, false);
canvas.addEventListener("mousemove", drawCircle, false);
} else {
canvas.removeEventListener("mousemove", drawCircle, false);
canvas.addEventListener("mousemove", drawTriangle, false);
}
lastPoint = { x: e.offsetX, y: e.offsetY };
startPoint = lastPoint;
};
function drawTriangle(e) {
// This doesn't work - triangle is not filled
e.preventDefault();
e.stopPropagation();
if (!isDrawing) return;
mx = e.offsetX;
my = e.offsetY;
// clear the canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// calculate the rectangle width/height based
// on starting vs current mouse position
var twidth = Math.abs(mx - startPoint.x) ;
var theight = Math.abs(my - startPoint.y) ;
// draw a new rect from the start position
// to the current mouse position
context.beginPath();
context.lineWidth = 3;
context.lineJoin = context.lineCap = 'round';
context.setLineDash([0, 0]);
context.globalAlpha = 1.0;
if ( mx >= startPoint.x ) {
context.moveTo(startPoint.x, startPoint.y );
context.lineTo(mx, my);
context.moveTo(mx-(2*twidth), my );
context.lineTo(mx, my);
context.moveTo(startPoint.x, startPoint.y );
context.lineTo(mx-(2*twidth), my );
} else {
context.moveTo(startPoint.x, startPoint.y );
context.lineTo(mx, my);
context.moveTo(mx+(2*twidth), my );
context.lineTo(mx, my);
context.moveTo(startPoint.x, startPoint.y );
context.lineTo(mx+(2*twidth), my );
}
context.closePath();
context.strokeStyle = 'red';
context.stroke();
context.fillStyle = 'rgba(25,50,75,0.5)';
context.fill();
}
function drawCircle(e) {
// This works
e.preventDefault();
e.stopPropagation();
if (!isDrawing) return;
mx = e.offsetX;
my = e.offsetY;
// clear the canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// calculate the rectangle width/height based
// on starting vs current mouse position
var cradius = Math.abs(mx - startPoint.x) ;
// draw a new rect from the start position
// to the current mouse position
context.beginPath();
context.lineWidth = 3;
context.lineJoin = context.lineCap = 'round';
context.setLineDash([0, 0]);
context.globalAlpha = 1.0;
context.strokeStyle = 'red';
context.arc(startPoint.x, startPoint.y, cradius, 0, 2 * Math.PI, false);
context.fillStyle = 'rgba(25,50,75,0.5)';
context.fill();
context.stroke();
}
canvas.onmouseup = function() {
isDrawing = false;
};
canvas.onmouseleave = function() {
isDrawing = false;
};
</script>
function drawTriangle(e) {
e.preventDefault();
e.stopPropagation();
if (!isDrawing) return;
// clear the canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// draw a new rect from the start position
// to the current mouse position
context.strokeStyle = 'red';
context.fillStyle = 'rgba(25,50,75,0.5)';
context.lineWidth = 3;
context.lineJoin = context.lineCap = 'round';
context.setLineDash([0, 0]);
context.globalAlpha = 1.0;
context.beginPath();
context.moveTo(startPoint.x, startPoint.y);
context.lineTo(e.offsetX, e.offsetY);
context.lineTo(startPoint.x * 2 - e.offsetX, e.offsetY);
context.closePath();
context.stroke();
context.fill();
}
The CanvasRenderingContext2D's fill() method fills a path with a given color. To be able to fill such a path, it must contain at least three points - which is fulfilled in your case.
The problem is the way you're creating the path:
context.moveTo(startPoint.x, startPoint.y );
context.lineTo(mx, my);
context.moveTo(mx-(2*twidth), my );
context.lineTo(mx, my);
context.moveTo(startPoint.x, startPoint.y );
context.lineTo(mx-(2*twidth), my );
By calling moveTo() again, you're essentially starting a new path - thus you have just three single lines hence nothing to fill.
Try making the path in one go:
context.moveTo(startPoint.x, startPoint.y );
context.lineTo(mx, my);
context.lineTo(mx-(2*twidth), my );
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var canrect = canvas.getBoundingClientRect();
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var lastPoint;
var startPoint;
var isDrawing = false;
var shapetype = 'triangle';
canvas.onmousedown = function(e) {
isDrawing = true;
if (shapetype == 'circle') {
canvas.removeEventListener("mousemove", drawTriangle, false);
canvas.addEventListener("mousemove", drawCircle, false);
} else {
canvas.removeEventListener("mousemove", drawCircle, false);
canvas.addEventListener("mousemove", drawTriangle, false);
}
lastPoint = {
x: e.offsetX,
y: e.offsetY
};
startPoint = lastPoint;
};
function drawTriangle(e) {
// This doesn't work - triangle is not filled
e.preventDefault();
e.stopPropagation();
if (!isDrawing) return;
mx = e.offsetX;
my = e.offsetY;
// clear the canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// calculate the rectangle width/height based
// on starting vs current mouse position
var twidth = Math.abs(mx - startPoint.x);
var theight = Math.abs(my - startPoint.y);
// draw a new rect from the start position
// to the current mouse position
context.beginPath();
context.lineWidth = 3;
context.lineJoin = context.lineCap = 'round';
context.setLineDash([0, 0]);
context.globalAlpha = 1.0;
if (mx >= startPoint.x) {
context.moveTo(startPoint.x, startPoint.y);
context.lineTo(mx, my);
context.lineTo(mx - (2 * twidth), my);
} else {
context.moveTo(startPoint.x, startPoint.y);
context.lineTo(mx, my);
context.lineTo(mx + (2 * twidth), my);
}
context.closePath();
context.strokeStyle = 'red';
context.stroke();
context.fillStyle = 'rgba(25,50,75,0.5)';
context.fill();
}
function drawCircle(e) {
// This works
e.preventDefault();
e.stopPropagation();
if (!isDrawing) return;
mx = e.offsetX;
my = e.offsetY;
// clear the canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// calculate the rectangle width/height based
// on starting vs current mouse position
var cradius = Math.abs(mx - startPoint.x);
// draw a new rect from the start position
// to the current mouse position
context.beginPath();
context.lineWidth = 3;
context.lineJoin = context.lineCap = 'round';
context.setLineDash([0, 0]);
context.globalAlpha = 1.0;
context.strokeStyle = 'red';
context.arc(startPoint.x, startPoint.y, cradius, 0, 2 * Math.PI, false);
context.fillStyle = 'rgba(25,50,75,0.5)';
context.fill();
context.stroke();
}
canvas.onmouseup = function() {
isDrawing = false;
};
canvas.onmouseleave = function() {
isDrawing = false;
};
#divContainer {
width: 100%;
height: 80%;
background: #ddd;
}
#divContentArea {
left: 0px;
top: 0px;
right: 0px;
bottom: 0px;
}
.canvas {
cursor: crosshair;
position: relative;
left: 0px;
top: 0px;
}
<div>
Click the button to select the shape type then click and drag mouse on the canvas below.
<BR>
<button type="button" onClick='shapetype="circle";'>Draw Circle</button>
<button type="button" onClick='shapetype="triangle";'>Draw Triangle</button>
<BR>
</div>
<div id="divContainer">
<div id="divContentArea">
<canvas id="canvas" class='canvas'>
Sorry, your browser does not support a canvas object.
</canvas>
</div>
</div>
I m working on a script that let clients draw the way they want to bend iron with detailed angles. everything is OK.
Now the client want's a 3D view of it. but i have no idea of how to do it (with three.js I guess)
I need a simple 3D view.
Here is what i did till now for 2D view, it's based on FabricJS:
$(function(){
var canvas = this.__canvas = new fabric.Canvas('c', { selection: false });
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
var Lines = Array();
var Points = Array();
var circleAngles = Array();
var anglesValues = Array();
function radianToDegrees(r){
r = r * 180/Math.PI;
if(r < 0) r = -r;
if(360 - r < r) r = 360 - r;
return 180 - parseFloat(r).toFixed(0);
}
function makeCircle(left, top, line1, line2) {
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 0,
radius: 8,
fill: '#000',
stroke: '#000'
});
c.hasControls = c.hasBorders = false;
c.line1 = line1;
c.line2 = line2;
return c;
}
function makeLine(coords) {
return new fabric.Line(coords, {
fill: 'red',
stroke: 'red',
strokeWidth: 2,
selectable: false,
evented: false,
});
}
function makeCircleAngle(angle, startAngle, endAngle){
if (angle == 1) color = 'red'; else color = '#003366';
circleAngle = new fabric.Circle({
radius: 20,
left: Lines[i].get('x1'),
top: Lines[i].get('y1'),
angle: 0,
startAngle: startAngle,
endAngle: endAngle,
strokeDashArray: [3, 2],
stroke: color,
fill: '',
selectable: false,
evented: false,
});
return circleAngle;
}
function makeText(text, x, y){
t = new fabric.Text(text, {
left: x, top: y, fontSize: 13, fill: '#003366', fontFamily: 'Arial',
selectable: false,
evented: false,
});
return t;
}
function drawLinesCanvas(){
$.each(Lines, function(i, e){
canvas.add(e);
})
}
function drawDotsCanvas(){
Points = Array();
p = makeCircle(Lines[0].get('x1'), Lines[0].get('y1'), null, Lines[0]);
Points.push(p)
canvas.add(p);
for(i = 0; i< Lines.length-1; i++){
p = makeCircle(Lines[i].get('x2'), Lines[i].get('y2'), Lines[i], Lines[i+1]);
Points.push(p)
canvas.add( p );
}
if (Lines.length-1 >= 0){
p = makeCircle(Lines[Lines.length-1].get('x2'), Lines[Lines.length-1].get('y2'), Lines[Lines.length-1]);
Points.push(p)
canvas.add( p );
}
}
function calculateAndDrawAngles(){
$.each(circleAngles, function(i, ce){
canvas.remove(ce);
})
$.each(Lines, function(i, l){
canvas.remove(l);
})
$.each(Points, function(i, p){
canvas.remove(p);
})
$.each(anglesValues, function(i, a){
canvas.remove(a);
})
if(Lines.length >= 2){
for(i=1; i<Lines.length; i++){
y11 = Lines[i].get('y1');
y12 = Lines[i].get('y2');
y21 = Lines[i-1].get('y1');
y22 = Lines[i-1].get('y2');
x11 = Lines[i].get('x1');
x12 = Lines[i].get('x2');
x21 = Lines[i-1].get('x1');
x22 = Lines[i-1].get('x2');
angle1 = Math.atan2(y11 - y12, x11 - x12);
angle2 = Math.atan2(y21 - y22, x21 - x22);
angle = angle1 - angle2;
if (angle < 0){
sStartAngle = Math.PI + angle1;
sEndAngle = angle2;
} else {
sStartAngle = angle1 - Math.PI;
sEndAngle = angle2;
}
myAngle = radianToDegrees(angle1 - angle2);
if(sStartAngle > sEndAngle) {
c = makeCircleAngle(1, sStartAngle, sEndAngle);
c1 = makeCircleAngle(2, sEndAngle, sStartAngle);
myAngleText = makeText(myAngle.toString()+'°', Lines[i].get('x1') +20, Lines[i].get('y1') + 20)
} else {
c = makeCircleAngle(2, sStartAngle, sEndAngle);
c1 = makeCircleAngle(1, sEndAngle, sStartAngle);
myAngleText = makeText(myAngle.toString()+'°', Lines[i].get('x1') - 20, Lines[i].get('y1') - 20)
}
circleAngles.push(c, c1);
canvas.add(c, c1);
canvas.add(myAngleText)
anglesValues.push(myAngleText);
}
drawLinesCanvas();
drawDotsCanvas();
}
}
canvas.on('object:moving', function(e) {
var p = e.target;
p.line1 && p.line1.set({ 'x2': p.left, 'y2': p.top });
p.line2 && p.line2.set({ 'x1': p.left, 'y1': p.top });
if (Lines.length > 1){
calculateAndDrawAngles();
}
});
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var zoom = canvas.getZoom();
zoom = zoom + delta/200;
if (zoom > 20) zoom = 5;
if (zoom < 0.01) zoom = 0.5;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
var vpt = this.viewportTransform;
if (zoom < 400 / 1000) {
this.viewportTransform[4] = 200 - 1000 * zoom / 2;
this.viewportTransform[5] = 200 - 1000 * zoom / 2;
} else {
if (vpt[4] >= 0) {
this.viewportTransform[4] = 0;
} else if (vpt[4] < canvas.getWidth() - 1000 * zoom) {
this.viewportTransform[4] = canvas.getWidth() - 1000 * zoom;
}
if (vpt[5] >= 0) {
this.viewportTransform[5] = 0;
} else if (vpt[5] < canvas.getHeight() - 1000 * zoom) {
this.viewportTransform[5] = canvas.getHeight() - 1000 * zoom;
}
}
});
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;
this.viewportTransform[4] += e.clientX - this.lastPosX;
this.viewportTransform[5] += e.clientY - this.lastPosY;
this.requestRenderAll();
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
}
});
canvas.on('mouse:up', function(opt) {
this.isDragging = false;
this.selection = true;
});
$('#addRight').on('click', function(){
fromPoint = Lines[Lines.length - 1];
Lines.push(makeLine([ fromPoint.get('x2'), fromPoint.get('y2'), fromPoint.get('x2') - 50, fromPoint.get('y2') + 50 ]))
calculateAndDrawAngles()
});
$('#addLeft').on('click', function(){
fromPoint = Lines[0];
Lines.unshift(makeLine([ fromPoint.get('x1') + 50, fromPoint.get('y1') + 50, fromPoint.get('x1'), fromPoint.get('y1') ]))
calculateAndDrawAngles()
});
function drawGrid(){
options = {
distance: 10,
width: c.width,
height: c.height,
param: {
stroke: '#ebebeb',
strokeWidth: 1,
selectable: false
}
},
gridLen = options.width / options.distance;
for (var i = 0; i < gridLen; i++) {
distance = i * options.distance,
horizontal = new fabric.Line([ distance, 0, distance, options.width], options.param),
vertical = new fabric.Line([ 0, distance, options.width, distance], options.param);
canvas.add(horizontal);
canvas.add(vertical);
if(i%5 === 0){
horizontal.set({stroke: '#cccccc'});
vertical.set({stroke: '#cccccc'});
};
canvas.sendBackwards(horizontal);
canvas.sendBackwards(vertical);
};
}
Lines = [makeLine([ 100, 50, 400, 50 ])];
drawGrid();
drawLinesCanvas();
drawDotsCanvas();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="c" width="500" height="400" style="border:1px solid #ccc;"></canvas>
<div class="text-center">
<button class="btn btn-info" id="addLeft">Ajouter un point à gauche</button>
<button class="btn btn-info" id="addRight">Ajouter un point à droite</button>
</div>
Just an option, when you can obtain the coordinates of points and use them to build a bended iron:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.setScalar(10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var obtained_data = [];
for (let i = 0; i < 10; i++) {
obtained_data.push(
new THREE.Vector2(i, Math.random() * 8 * 0.5 - 0.5) // fill the data with example coordinates
);
}
//console.log(obtained_data);
var dataLength = obtained_data.length;
var geom = new THREE.PlaneBufferGeometry(1, 10, dataLength - 1, 10); // size on Y and its segmentation is up to you
geom.rotateX(Math.PI * 0.5);
var pos = geom.getAttribute("position");
for (let i = 0; i < pos.count; i++) {
let idx = i % dataLength;
let x = obtained_data[idx].x;
let y = obtained_data[idx].y;
pos.setXY(i, x, y);
}
pos.needsUpdate = true;
geom.computeVertexNormals();
var mat = new THREE.MeshBasicMaterial({
color: "aqua",
wireframe: true
});
var iron = new THREE.Mesh(geom, mat);
scene.add(iron);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
How do i bind onclick event to piechart segment?
https://github.com/sauminkirve/HTML5/blob/master/PieChart/piechart.html
A pie chart segment is really a wedge. You have several ways to hit-test a wedge.
One way is the math way:
Test if the mouse is within the radius of a circle created by the wedges.
If the radius test is true, then calculate the angle of the mouse versus the circle's centerpoint.
Compare that angle to each wedge. If the angle is between the starting and ending angle of a specific wedge's arc, then the mouse is inside that wedge.
Another way is to use canvas's built in path hit-testing method: isPointInPath
Redefine one wedge. There's no need to actually stroke or fill that wedge. Just do the commands from beginPath to closePath.
Use context.isPointInPath(mouseX,mouseY) to hit-test if the mouse is inside that wedge.
If isPointInPath returns true, you've discovered the wedge under the mouse. If not, then redefine & hit-test each of the other wedges.
Here's something I coded a while back that hit-tests the wedges of a pie chart when hovering and moves the wedge out of the pie when a wedge is clicked.
It uses the isPointInPath method to do the hit-testing:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.lineJoin = "round";
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
function Wedge(cx, cy, radius, startAngleDeg, endAngleDeg, fill, stroke, linewidth) {
this.cx = cx;
this.cy = cy;
this.radius = radius;
this.startAngle = startAngleDeg * Math.PI / 180;
this.endAngle = endAngleDeg * Math.PI / 180;
this.fill = fill;
this.stroke = stroke;
this.lineWidth = linewidth;
this.offsetX = 0;
this.offsetY = 0;
this.rr = radius * radius;
this.centerX = cx;
this.centerY = cy;
this.midAngle = this.startAngle + (this.endAngle - this.startAngle) / 2;
this.offsetDistance = 15;
this.explodeX = this.offsetDistance * Math.cos(this.midAngle);
this.explodeY = this.offsetDistance * Math.sin(this.midAngle);
this.isExploded = false;
};
Wedge.prototype.draw = function(fill, stroke) {
this.define();
this.fillStroke(fill, stroke);
ctx.beginPath();
ctx.arc(this.cx, this.cy, this.radius, 0, Math.PI * 2);
ctx.closePath();
ctx.lineWidth = 0.50;
ctx.stroke();
}
Wedge.prototype.fillStroke = function(fill, stroke) {
ctx.fillStyle = fill || this.fill;
ctx.fill();
ctx.strokeStyle = stroke, this.stroke;
ctx.lineWidth = this.lineWidth;
ctx.stroke();
}
Wedge.prototype.define = function() {
var x = this.cx + this.offsetX;
var y = this.cy + this.offsetY;
ctx.beginPath();
ctx.arc(x, y, this.radius, this.startAngle, this.endAngle);
ctx.lineTo(x, y);
ctx.closePath();
}
Wedge.prototype.ptAtAngle = function(radianAngle) {
var xx = (this.cx + this.offsetX) + this.radius * Math.cos(radianAngle);
var yy = (this.cy + this.offsetY) + this.radius * Math.sin(radianAngle);
return ({
x: x,
y: y
});
}
Wedge.prototype.explode = function(isExploded) {
this.isExploded = isExploded;
this.offsetX = isExploded ? this.explodeX : 0;
this.offsetY = isExploded ? this.explodeY : 0;
this.draw();
}
Wedge.prototype.isPointInside = function(x, y) {
var dx = x - (this.cx + this.offsetX);
var dy = y - (this.cy + this.offsetY);
if (dx * dx + dy * dy > this.rr) {
return (false);
}
var angle = (Math.atan2(dy, dx) + Math.PI * 2) % (Math.PI * 2);
return (angle >= this.startAngle && angle <= this.endAngle);
}
Wedge.prototype.marker = function(pos) {
ctx.beginPath();
ctx.arc(pos.x, pos.y, 3, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = "red";
ctx.fill();
}
function handleMouseDown(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
clear();
for (var i = 0; i < wedges.length; i++) {
var wedge = wedges[i].wedge;
if (wedge.isPointInside(mouseX, mouseY)) {
wedge.explode(!wedge.isExploded);
}
wedge.draw();
}
}
function handleMouseUp(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mouseup stuff here
isDown = false;
}
function handleMouseOut(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mouseOut stuff here
isDown = false;
}
function handleMouseMove(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
for (var i = 0; i < wedges.length; i++) {
var wedge = wedges[i].wedge;
if (wedge.isPointInside(mouseX, mouseY)) {
wedge.draw("black");
} else {
wedge.draw();
}
}
}
$("#canvas").mousedown(function(e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function(e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function(e) {
handleMouseUp(e);
});
$("#canvas").mouseout(function(e) {
handleMouseOut(e);
});
function clear() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
var PI2 = Math.PI * 2;
var cx = 150;
var cy = 150;
var r = 100;
var line = 2;
var stroke = "black";
var wedges = [];
wedges.push({
percent: 18,
fill: "red"
});
wedges.push({
percent: 30,
fill: "blue"
});
wedges.push({
percent: 25,
fill: "green"
});
wedges.push({
percent: 13,
fill: "purple"
});
wedges.push({
percent: 14,
fill: "gold"
});
var rAngle = 0;
for (var i = 0; i < wedges.length; i++) {
var wedge = wedges[i];
var angle = 360 * wedge.percent / 100;
wedge.wedge = new Wedge(cx, cy, r, rAngle, rAngle + angle, wedge.fill, "black", 1);
wedge.wedge.draw();
rAngle += angle;
}
window.onscroll = function(e) {
var BB = canvas.getBoundingClientRect();
offsetX = BB.left;
offsetY = BB.top;
}
body {
background-color: ivory;
}
#canvas {
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Hover wedge to highlight it<br>Click wedge to explode that wedge</h4>
<canvas id="canvas" width=300 height=300></canvas>
I think I'm almost there, but I could be wrong. I'm trying to get the grouped arc sectors to animate as a whole circle in one direction. How do I reference the createWheel function inside the animateWheel function?
Here is the code:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Circle - Arc 3</title>
<style>
h1 {
font-family:Arial, Helvetica, sans-serif;
font-size: 1.5em;
color:#333;
}
#canvas1{ background-color:#699;}
</style>
</head>
<body>
<h1>Circle - Arc</h1>
<canvas id="canvas1" width="600" height="600"> Your Browser does not support HTML 5
</canvas>
<script>
// arc sectors vars
var centerX = 200;
var centerY = 200;
var radius = 100;
var fullCircleDegree = 360;
// Closure Function to ceate dynamic arc sectors
var arcSectors = function(num) { // The outer function defines a variable called "num"
var getNum = function() {
return 360 / num; // The inner function has access to the "num" variable of the outer function
}
return getNum; // Return the inner function, thereby exposing it to outer scopes
},
createArcSectors = arcSectors(7);
var rotateAngle = createArcSectors() * Math.PI/180;
var startAngle = 0 * Math.PI/180;
var endAngle = createArcSectors() * Math.PI/180;
var animateRot = 0;
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
window.onload = function () {
createWheel();
}
function createWheel() {
var theCanvas = document.getElementById('canvas1');
var context = theCanvas.getContext('2d');
context.clearRect(0, 0, canvas1.width, canvas1.height);
context.arc(centerX, centerY, radius, startAngle, endAngle, false);
// create arc sectors
for (i = 0; i < 7; i++) {
context.translate(centerX, centerY);
context.rotate(rotateAngle);
context.translate(-centerX, -centerY);
context.beginPath();
context.moveTo(centerX, centerY);
context.lineTo(centerX + radius, centerY);
context.arc(centerX, centerY, radius, startAngle, endAngle, false);
context.closePath();
context.stroke();
}
animateWheel();
}
function animateWheel() {
var theCanvas = document.getElementById('canvas1');
var context = theCanvas.getContext('2d');
//rotateAngle = animateRot * Math.PI / 180;
rotateAngle = .002;
console.log(rotateAngle);
animateRot += .002;
if (rotateAngle > 360) {
animateRot -= 1;
}
requestAnimFrame(function() {
animateWheel();
});
}
</script>
</body>
</html>
The requestAnimationFrame design pattern looks like this:
function animate() {
// request a new animation frame as soon as possible
requestAnimFrame(animate);
// reset any values that need to change with every frame
rotation+=PI2/120;
// do the drawing
drawWheel(cx,cy,rotation);
}
Here's a demo of your code (slightly refactored): http://jsfiddle.net/m1erickson/LydNg/
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var PI2=Math.PI*2;
var cx=100;
var cy=100;
var radius=30;
var rotation=-Math.PI/2;
animate();
function drawWheel(cx,cy,rotation){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(cx,cy);
ctx.rotate(rotation);
ctx.beginPath();
ctx.arc(0,0,radius,0,PI2,false);
ctx.closePath();
for(var i=0;i<7;i++){
var r=PI2/7*i;
ctx.moveTo(0,0);
ctx.lineTo(radius*Math.cos(r),radius*Math.sin(r));
}
ctx.stroke();
ctx.restore();
}
var fps = 60;
function animate() {
setTimeout(function() {
requestAnimFrame(animate);
// Drawing code goes here
rotation+=PI2/120;
drawWheel(cx,cy,rotation);
}, 1000 / fps);
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=350 height=350></canvas>
</body>
</html>
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