Rotated triangles change their center HTML5 - html

I have a problem with my program. I wanted to rotate triangle few times and draw it every time I rotate, but triangle changes it's place( I wanted it on the center of the screen).
The triangle drawing is in draw function.
<!DOCTYPE html>
<html>
<head>
<script>
var ctx, canvas;
window.requestAnimFrame = (function(callback) {
return (window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame) &&
function(callback) {
window.setTimeout(callback, 1000/1 );
};
})();
window.onload = function(){
canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
//sciana
for(var i = 0; i < 6; i++){
drawLine(i);
}
}
function drawLine(a){
var bok = 400, xw = canvas.width/2, yw = 10, odstep = 10;
var tX = 10/Math.sqrt(3);
ctx.rotate(Math.PI /6); //here I rotate
ctx.save();
for(var i = xw; i >= xw - bok/2; i-=tX){
var r = Math.floor( 255 * Math.random());
var g = Math.floor( 255 * Math.random());
var b = Math.floor( 255 * Math.random());
ctx.translate(10/Math.sqrt(3), 10);
ctx.beginPath();
ctx.fillStyle = "rgb("+r+","+g+","+b+")";
ctx.arc(xw, 0, 6, 0, 2 * Math.PI, false);
ctx.fill();
ctx.closePath();
}
ctx.restore();
ctx.save();
for(var i = xw; i >= xw - bok/2; i-=10/Math.sqrt(3)){
var r = Math.floor( 255 * Math.random());
var g = Math.floor( 255 * Math.random());
var b = Math.floor( 255 * Math.random());
ctx.translate(-10/Math.sqrt(3), 10);
ctx.beginPath();
ctx.fillStyle = "rgb("+r+","+g+","+b+")";
ctx.arc(xw, 0, 6, 0, 2 * Math.PI, false);
ctx.fill();
ctx.closePath();
}
ctx.restore();
ctx.save();
xw = xw - bok/2 ;
yw = bok*Math.sqrt(3)/2;
for(var i = xw; i <= xw + bok; i+=10){
var r = Math.floor( 255 * Math.random());
var g = Math.floor( 255 * Math.random());
var b = Math.floor( 255 * Math.random());
ctx.translate(10,0);
ctx.beginPath();
ctx.fillStyle = "rgb("+r+","+g+","+b+")";
ctx.arc(xw, yw, 6, 0, 2 * Math.PI, false);
ctx.fill();
ctx.closePath();
}
ctx.restore();
requestAnimFrame(drawLine);
}
</script>
</head>
<body>
<canvas id="myCanvas" width="600" height="600" style="background-color:#D0D0D0">
Twoja przeglądarka nie obsługuje elementu Canvas.
</canvas>
</body>
</html>

Related

Canvas Image rearrangement and rotation (center)

Canvas has an image and rectangle. Image should be rotated around center of canvas therefore I use translate to achive center but translate won't allow to drag the objects(dont know why).
When I drag, image and rectangle should be draggable together. When I rotate, only image should be rotatable and rectangle should be static(intact to rotation).
Anyone can help on the following code?
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvasOffset = $("#canvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var startX;
var startY;
var isDown = false;
var pi2 = Math.PI * 2;
var resizerRadius = 6;
var rr = resizerRadius * resizerRadius;
var draggingResizer = {
x: 0,
y: 0
};
var imageX = 0;
var imageY = 0;
var imageWidth, imageHeight, imageRight, imageBottom;
var draggingImage = false;
var startX;
var startY;
var img = new Image();
img.onload = function() {
imageWidth = img.width;
imageHeight = img.height;
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight
draw();
}
img.src = "http://www.w3.org/html/logo/downloads/HTML5_Logo_128.png";
var x = canvas.width / 2, y = canvas.height / 2;
function draw() {
canvas.width = canvas.width;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
//I have problem here!
//ctx.translate(x + imageX, y + imageY);
ctx.rotate($('#rvalue').val() * Math.PI / 180);
ctx.drawImage(img, imageX, imageY);
ctx.restore();
ctx.rect(imageX + 160, imageY + 30, 120, 40);
ctx.strokeStyle = "red";
ctx.stroke();
}
function hitImage(x, y) {
return (x > imageX && x < imageX + imageWidth && y > imageY && y < imageY + imageHeight);
}
function handleMouseDown(e) {
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
draggingImage = hitImage(startX, startY);
}
function handleMouseUp(e) {
draggingImage = false;
draw();
}
function handleMouseOut(e) {
handleMouseUp(e);
}
function handleMouseMove(e) {
if (draggingImage) {
imageClick = false;
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
var dx = mouseX - startX;
var dy = mouseY - startY;
imageX += dx;
imageY += dy;
imageRight += dx;
imageBottom += dy;
startX = mouseX;
startY = mouseY;
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);
});
$("#rotate").click(function(event) {
var c = parseInt($('#rvalue').val());
$('#rvalue').val(c + 90);
draw();
});
<!DOCTYPE HTML>
<html lang="en"><head>
<meta charset="UTF-8" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<style type="text/css">#canvas { border: 1px solid #000; }</style>
</head>
<body>
<input type=text value="0" id="rvalue" />
<button id=rotate>Rotate</button><br>
<canvas id="canvas" width=600 height=400></canvas>
</body>
</html>
I'm a bit rusty with canvas, but would it help to translate, rotate, translate back, and THEN draw?
ctx.translate( centerx, centery );
ctx.rotate( $('#rvalue').val() * Math.PI / 180 );
ctx.translate( -centerx, -centery );
ctx.drawImage(img, imageX, imageY);

Make canvas element orbit around/follow mouse

http://jsfiddle.net/CBY7p/1/
I want to get the cyan dot spin around in the canvas chasing my mouse, but like my mouse has gravity. Think of it like the mouse is a planet and the object is a comet. I tried this code, but it just makes the cyan dot spin like crazy and not follow the mouse too much.
<div class="section">
<div id="intro">
<div id="mouse" style="border-radius: 50%; position: absolute;height: 20px;width: 20px;background-color: blue;"></div>
<canvas id="canvas" style="background:black;">
</canvas>
<script>
var canvas = document.getElementById("canvas");
canvas.width = $(window).width();
canvas.height = $(window).height();
</script>
<script>
var canvas = document.querySelector("#canvas")
var ctx = canvas.getContext("2d");
var mouseX;
var mouseY;
var kule = {
cx : 100,
cy : 100,
vy : 2,
vx : 2,
r : 5,
e : 1,
color : "cyan"
};
function draw() {
var image = new Image();
image.src = "Player.png";
ctx.drawImage(image, 100, 100);
var boundsX = canvas.width;
var boundsY = canvas.height;
//ctx.clearRect(0, 0, bounds, bounds);
ctx.fillStyle = kule.color;
ctx.beginPath();
ctx.arc(kule.cx, kule.cy, kule.r, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
var deltaX = mouseX - kule.cx;
var deltaY = mouseY - kule.cy;
kule.vy = kule.vy + deltaX/1000;
kule.vx = kule.vx + deltaY/1000;
kule.cx = kule.cx + kule.vx;
kule.cy = kule.cy + kule.vy;
if (kule.cy + kule.r >= boundsY) {
kule.vy = -kule.vy * kule.e;
kule.vy = -(Math.abs(kule.vy)) * kule.e;
}
if (kule.cx + kule.r >= boundsX) {
kule.vx = -kule.vx * kule.e;
kule.vx = -(Math.abs(kule.vx)) * kule.e;
}
if (kule.cy - kule.r <= 0) {
kule.vy = kule.vy * kule.e;
kule.vy = (Math.abs(kule.vy)) * kule.e;
}
if (kule.cx - kule.r <= 0) {
kule.vx = kule.vx * kule.e;
kule.vx = (Math.abs(kule.vx)) * kule.e;
}
}
setInterval(draw, 10);
$(document).on("mousemove",function(event){
mouseX = event.pageX;
mouseY = event.pageY;
$("#mouse").animate(
{
left:mouseX-10,
top:mouseY-10
},0)});
</script>
</div>
I removed some pieces of your code to simplify my answer, but here's the idea :
var startTime = (new Date()).getTime();
var rotationSpeed = 500; // Milliseconds for a full turn
var orbitRadius = 75;
function draw() {
var boundsX = canvas.width;
var boundsY = canvas.height;
ctx.clearRect(0, 0, boundsX, boundsY);
ctx.fillStyle = kule.color;
var currentTime = (new Date()).getTime();
var passedTime = currentTime - startTime;
var angle = Math.PI * 2 * (passedTime / rotationSpeed);
ctx.beginPath();
ctx.arc(mouseX + Math.cos(angle) * orbitRadius, mouseY + Math.sin(angle) * orbitRadius, kule.r, 0, Math.PI * 2);
ctx.fill();
}
Link to jsfiddle

Drag & drop image on canvas image

I want to drag & drop text above image. For that I am using canvas. I am using this code
<img id="scream" src="http://127.0.0.1/demo/images.jpg" alt="The Scream" style="display:none;" width="220" height="277"><p>Canvas:</p>
<canvas id="canvas" width="300" height="300" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
var c=document.getElementById("canvas");
var ctx1=c.getContext("2d");
var img=document.getElementById("scream");
ctx1.drawImage(img,10,10);
var canvas;
var ctx;
var x = 75;
var y = 50;
var dx = 5;
var dy = 3;
var WIDTH = 400;
var HEIGHT = 300;
var dragok = false,
text = "Hey there im moving!",
textLength = (text.length * 14)/2;
function rect(x,y,w,h) {
ctx.font = "14px Arial";
ctx.strokeText("Hey there im a moving!!", x, y);
}
function clear() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
}
function init() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
return setInterval(draw, 10);
}
function draw() {
clear();
ctx.fillStyle = "#FAF7F8";
ctx.fillStyle = "#444444";
rect(x - 15, y + 15, textLength, 30);
}
function myMove(e){
if (dragok){
x = e.pageX - canvas.offsetLeft;
y = e.pageY - canvas.offsetTop;
}
}
function myDown(e){
if (e.pageX < x + textLength + canvas.offsetLeft && e.pageX > x - textLength + canvas.offsetLeft && e.pageY < y + 15 + canvas.offsetTop &&
e.pageY > y -15 + canvas.offsetTop){
x = e.pageX - canvas.offsetLeft;
y = e.pageY - canvas.offsetTop;
dragok = true;
canvas.onmousemove = myMove;
}
}
function myUp(){
dragok = false;
canvas.onmousemove = null;
}
init();
canvas.onmousedown = myDown;
canvas.onmouseup = myUp;
I either able to show image or drag & drop text but I want both, please help me where I am wrong. You can check here:- http://jsfiddle.net/FWdSv/11/
When you're clearing the canvas, you're also clearing your image.
So the easy fix is to redraw the image in your draw function:
function draw() {
clear();
ctx.drawImage(img,0,0);
ctx.fillStyle = "#FAF7F8";
ctx.fillStyle = "#444444";
rect(x - 15, y + 15, textLength, 30);
}
Alternatively:
You could display your image underneath your canvas so it's not affected when you clear the canvas.

Ball movement path in canvas, and other ideas you may have?

I've created this animation for my project that had to use any form of physics.
I am a total beginner, too :) Anyway, this is my project now :
Bouncing Balls
You can setup gravity and force, then click play, and just drag and drop to shoot the balls. You can change the values and hit update too see an effect.
My question is, how can I create an effect that when I press ratio button (for example) I can see the path that ball makes? Is it complicated? As I was saying I am a beginner, so no complex code for me :)
Also, doyou have any ideas to make the project better? Any additional "physics" effects? Or maybe you know a website that shows tutorials for simile (please) effects made in HTML5/js so I can add additional effects to my project.
One possibility (as you're clearing the canvas each frame) would be to draw ball paths onto a secondary canvas, which would not be cleared each frame. Then, when you come to clear the first frame, render the second frame after clearing, and before rendering the balls.
The second canvas would of course have to be the same dimensions as the first, so that all of the ball points line up correctly. The second canvas should also have a z-index lower than the first, so that it is only shown when you specifically render it to the first canvas (i.e. when the radio button is checked).
To decrease any lag while the radio is not checked, you could skip drawing the ball paths to the second canvas, although I don't think you would see any great increase in performance.
On each frame update, you would mark the position of each ball with a pixel, or line (from the previous position to the current) on the second canvas.
Looking at your code, you seem pretty competent, so I've skipped writing an example as I think this would be good experience for you :)
Modified 'script.js' source demonstrating solution
window.onload = function(){
$("#canvas").hide();
var howManyPaths = 0;
var showPath=false;
// SLIDERS
var gravitySlider = document.getElementById('gravitySlider');
var gravityVal = document.getElementById('gravityValue');
gravitySlider.onchange = function(){
gravityVal.value = gravitySlider.value;
}
gravityVal.onkeyup = function(){
gravitySlider.value = gravityVal.value;
}
var forceSlider = document.getElementById('forceSlider');
var forceValue = document.getElementById('forceValue');
forceSlider.onchange = function(){
forceValue.value = forceSlider.value;
}
forceValue.onkeyup = function(){
forceSlider.value = forceValue.value;
}
// GLOBAL VARIABLES
var test = false;
var gravityCount = $("#gravity").val();
var forceCount = $("#rectangles").val();
// CSS :
var playCSS = document.getElementById("play");
var restartCSS = document.getElementById("restart");
var clickableCSS = document.getElementById("setup");
var clickableBG = document.getElementById("img");
//restartCSS.style.visibility="hidden";
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvas2 = document.getElementById("canvas2");
var ctx2 = canvas2.getContext("2d");
//var ctx;
var gravity = 9.86;
var forceFactor = 0.5;
var mouseDown = false;
var balls = new Array();
var mousePos = new Array();
// EVENT HANDLER
function onMouseDown(evt){
mouseDown = true;
mousePos['downX'] = evt.pageX;
mousePos['downY'] = evt.pageY;
}
function onMouseUp(evt){
mouseDown = false;
setup.style.visibility="visible";
if(test == true && !( mousePos['downX'] < 200 && mousePos['downY'] < 150) ){
restartCSS.style.visibility="visible";
forceFactor = forceCount;
balls.push(new ball(mousePos["downX"],
mousePos["downY"],
(evt.pageX - mousePos["downX"]) * forceFactor,
(evt.pageY - mousePos["downY"]) * forceFactor,
10 + (Math.random() * 10),
0.8,
randomColor()
));
}
ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
}
function onMouseMove(evt){
mousePos['currentX'] = evt.pageX;
mousePos['currentY'] = evt.pageY;
}
function resizeWindow(evt){
//canvas.height = 960;
//canvas.width = 720;
canvas.height = $(window).height()-6;
canvas.width = $(window).width();
canvas2.height = $(window).height()-6;
canvas2.width = $(window).width();
}
$(document).mousedown(onMouseDown);
$(document).mouseup(onMouseUp);
$(document).mousemove(onMouseMove);
$(window).bind("resize", resizeWindow);
// GRAPHICS CODE
function circle(x, y, r, col){
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.closePath;
// fill
ctx.fillStyle = col;
ctx.fill();
// stroke
ctx.lineWidth = r * 0.1;
ctx.strokeStyle = "#000000";
ctx.stroke();
}
function circlePath(x, y)
{
ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
ctx2.fillStyle = '#3f4043';
ctx2.fillRect(x, y, 5, 5);
ctx2.strokeStyle = "black";
ctx2.strokeRect(x, y, 5, 5);
}
function randomColor(){
var letter = "0123456789ABCDEF".split("");
var color = "#";
for(var i=0; i<6; i++){
color += letter[Math.round(Math.random()*15)];
}
return color;
}
function arrow(fromX, fromY, toX, toY, color){
// path
ctx.beginPath();
var headLen = 10;
var angle = Math.atan2(toY - fromY, toX - fromX);
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.lineTo(toX - headLen * Math.cos(angle - Math.PI/6), toY - headLen * Math.sin(angle - Math.PI/6));
ctx.moveTo(toX, toY);
ctx.lineTo(toX - headLen * Math.cos(angle + Math.PI/6), toY - headLen * Math.sin(angle + Math.PI/6));
// style
ctx.lineWith = 1;
ctx.strokeStyle = color;
ctx.lineCap = "butt";
ctx.stroke();
}
function drawBall(){
// Gravity
gravity = gravityCount;
this.speedY += gravity * 0.5; // v = a * t
this.x += this.speedX * 0.05; // s = v * t
this.y += this.speedY * 0.05;
// prawa ściana
if(this.x + this.r > canvas.width){
this.x = canvas.width - this.r;
this.speedX *= -1 * this.bounce;
}
// lewa ściana
if(this.x - this.r < 0){
this.x = this.r;
this.speedX *= -1 * this.bounce;
}
// dolna ściana
if(this.y + this.r > canvas.height){
this.y = canvas.height - this.r;
this.speedY *= -1 * this.bounce;
}
// górna ściana
if(this.y - this.r < 0){
this.y = this.r;
this.speedY *= -1 * this.bounce;
}
// zwalnianie na ziemi
if (this.speedX > 0.25){
this.speedX -= 0.25;
if (this.speedY > 0.25)
this.speedY -= 0.25;
}
if (this.speedX < -0.25){
this.speedX += 0.25;
//if (this.speedY < -0.25)
// this.speedY += 0.25;
}
circle(this.x, this.y, this.r, this.col);;
}
// OBJECTS
function ball(positionX, positionY, sX, sY, radius, b, color){
this.x = positionX;
this.y = positionY;
this.speedX = sX;
this.speedY = sY;
this.r = radius;
this.bounce = b;
this.col = color;
this.draw = drawBall;
}
//GAME LOOP
function gameLoop(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
//grab the context from your destination canvas
//if path drawing is enabled, first draw the path canvas to the display canvas
if (showPath) ctx.drawImage(canvas2,0,0);
if(mouseDown == true){
// ctx.clearRect(0, 0, canvas.width, canvas.height); /* !important !!!!!!!!!!!!!!! */
arrow(mousePos['downX'], mousePos['downY'], mousePos['currentX'], mousePos['currentY'], "red");
}
for(var i=0; i<balls.length; i++){
balls[i].draw();
if (i==balls.length-1) {
//draw path
ctx2.fillStyle = '#3f4043';
ctx2.fillRect(balls[i].x, balls[i].y, 5, 5);
ctx2.strokeStyle = "black";
ctx2.strokeRect(balls[i].x, balls[i].y, 5, 5);
}
}
ctx.fillStyle = "#000000";
ctx.font = "15px Arial";
ctx.fillText("Balls: " + balls.length + " " + gravityCount + " " + forceCount + " " + howManyPaths, 10, canvas.height -10);
}
// START THE GAME
function init(){
//$("#setup").hide();
$("#canvas").show();
$("#canvas2").hide();
ctx = $('canvas')[0].getContext("2d");
canvas.height = $(window).height()-6;
canvas.width = $(window).width();
//canvas.width = 960;
//canvas.height = 720;
canvas2.height = $(window).height()-6;
canvas2.width = $(window).width();
return setInterval(gameLoop, 10);
}
$("#play").click(function() {
test = true;
playCSS.style.visibility="hidden";
gravityCount = $("#gravitySlider").val();
forceCount = $("#forceSlider").val();
init();
});
$("#restart").click(function() {
window.location.href="index.html";
});
$("#refresh").click(function() {
gravityCount = $("#gravitySlider").val();
forceCount = $("#forceSlider").val();
});
$("#showPath").click(function() {
showPath=true;
});
$("#hidePath").click(function() {
showPath=false;
});
}

Html 5 Canvas complete arrowhead

I'm using the wPaint plugin and I am attempting to add a few more features. What I need is a drawn line to end with an "arrowhead". I have tried just about everything I could think of, but I can only get half of the arrow ( imagine <-----, but the head only extends to the bottom or the top, but never both directions.)
Here is the function for drawing the line (with the half arrowhead):
drawArrowMove: function(e, _self)
{
var xo = _self.canvasTempLeftOriginal;
var yo = _self.canvasTempTopOriginal;
if(e.pageX < xo) { e.x = e.x + e.w; e.w = e.w * -1}
if(e.pageY < yo) { e.y = e.y + e.h; e.h = e.h * -1}
_self.ctxTemp.lineJoin = "round";
_self.ctxTemp.beginPath();
_self.ctxTemp.moveTo(e.x, e.y);
_self.ctxTemp.lineTo(e.x + e.w, e.y + e.h);
_self.ctxTemp.closePath();
_self.ctxTemp.moveTo(e.x, e.y);
_self.ctxTemp.lineTo(15,10);
_self.ctxTemp.stroke();
}
Any help/ideas/tips would be helpful.
Thanks.
This is how to create a line object that draws arrowheads on both ends
The interesting part is calculating the angle of the arrowheads like this:
var startRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
startRadians+=((this.x2>=this.x1)?-90:90)*Math.PI/180;
var endRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
endRadians+=((this.x2>=this.x1)?90:-90)*Math.PI/180;
The rest is just drawing the line and 2 triangles for arrowheads the calculated rotations
Line.prototype.drawArrowhead=function(ctx,x,y,radians){
ctx.save();
ctx.beginPath();
ctx.translate(x,y);
ctx.rotate(radians);
ctx.moveTo(0,0);
ctx.lineTo(5,20);
ctx.lineTo(-5,20);
ctx.closePath();
ctx.restore();
ctx.fill();
}
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/Sg7EZ/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
function Line(x1,y1,x2,y2){
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
}
Line.prototype.drawWithArrowheads=function(ctx){
// arbitrary styling
ctx.strokeStyle="blue";
ctx.fillStyle="blue";
ctx.lineWidth=1;
// draw the line
ctx.beginPath();
ctx.moveTo(this.x1,this.y1);
ctx.lineTo(this.x2,this.y2);
ctx.stroke();
// draw the starting arrowhead
var startRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
startRadians+=((this.x2>this.x1)?-90:90)*Math.PI/180;
this.drawArrowhead(ctx,this.x1,this.y1,startRadians);
// draw the ending arrowhead
var endRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
endRadians+=((this.x2>this.x1)?90:-90)*Math.PI/180;
this.drawArrowhead(ctx,this.x2,this.y2,endRadians);
}
Line.prototype.drawArrowhead=function(ctx,x,y,radians){
ctx.save();
ctx.beginPath();
ctx.translate(x,y);
ctx.rotate(radians);
ctx.moveTo(0,0);
ctx.lineTo(5,20);
ctx.lineTo(-5,20);
ctx.closePath();
ctx.restore();
ctx.fill();
}
// create a new line object
var line=new Line(50,50,150,150);
// draw the line
line.drawWithArrowheads(context);
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
This goes wrong for vertical lines.
Try
var line=new Line(50,50,50,275)
As addition to markE's answer combined with user1707810 comment:
Both blocks of (start/end radians):
- ((this.x2 > this.x1)?-90:90)*Math.PI/180;
should be changed to :
- ((this.x2 >= this.x1)?-90:90)*Math.PI/180;
Simpler version
key difference. Using Math.atan2 removes the need for if
This one also puts the arrowheads at the ends of the line rather than past the end of the line
In other words
this
start end
|<------->|
vs this
<|---------|>
function arrow(ctx, x1, y1, x2, y2, start, end) {
var rot = -Math.atan2(x1 - x2, y1 - y2);
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
if (start) {
arrowHead(x1, y1, rot);
}
if (end) {
arrowHead(x2, y2, rot + Math.PI);
}
}
function arrowHead(x, y, rot) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(rot);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(-5, -12);
ctx.lineTo(5, -12);
ctx.closePath();
ctx.fill();
ctx.restore();
}
// test it -------
var ctx = document.createElement("canvas").getContext("2d");
document.body.appendChild(ctx.canvas);
// draw some arrows
ctx.save();
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
for (var ii = 0; ii <= 12; ++ii) {
var u = ii / 12;
var color = hsl(u * 360, 1, 0.5);
ctx.fillStyle = color;
ctx.strokeStyle = color;
var a = u * Math.PI;
var x = Math.cos(a) * 120;
var y = Math.sin(a) * 70;
arrow(ctx, -x, -y, x, y, true, true);
// draw the end points to see the arrowheads match
ctx.fillStyle = "#000";
ctx.fillRect(-x - 1, -y - 1, 3, 3);
ctx.fillRect( x - 1, y - 1, 3, 3);
}
ctx.restore();
function hsl(h, s, l) {
return `hsl(${h},${s * 100}%,${l * 100}%)`;
}
canvas { border: 1px solid black; }
One problem with the above solution is if you thicken the line stroke it would poke through the arrowhead. It's not hard to fix but you'd have to compute the length of the line in pixels, then subtract the size of the arrowhead from both sides.
something like this
function arrow(ctx, x1, y1, x2, y2, start, end) {
var dx = x2 - x1;
var dy = y2 - y1;
var rot = -Math.atan2(dx, dy);
var len = Math.sqrt(dx * dx + dy * dy);
var arrowHeadLen = 10;
ctx.save();
ctx.translate(x1, y1);
ctx.rotate(rot);
ctx.beginPath();
ctx.moveTo(0, start ? arrowHeadLen : 0);
ctx.lineTo(0, len - (end ? arrowHeadLen : 0));
ctx.stroke();
if (end) {
ctx.save();
ctx.translate(0, len);
arrowHead(ctx);
ctx.restore();
}
if (start) {
ctx.rotate(Math.PI);
arrowHead(ctx);
}
ctx.restore();
}
function arrowHead(ctx) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(-5, -12);
ctx.lineTo(5, -12);
ctx.closePath();
ctx.fill();
}
// test it -------
var ctx = document.createElement("canvas").getContext("2d");
document.body.appendChild(ctx.canvas);
// draw some arrows
ctx.save();
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
for (var ii = 0; ii < 12; ++ii) {
var u = ii / 12;
var color = hsl(u * 360, 1, 0.5);
ctx.fillStyle = color;
ctx.strokeStyle = color;
var a = u * Math.PI;
var x = Math.cos(a) * 120;
var y = Math.sin(a) * 70;
arrow(ctx, -x, -y, x, y, true, true);
ctx.fillStyle = "#000"; // mark the ends so we can see where they are
ctx.fillRect(-x - 1, -y - 1, 3, 3);
ctx.fillRect( x - 1, y - 1, 3, 3);
}
ctx.restore();
function hsl(h, s, l) {
return `hsl(${h},${s * 100}%,${l * 100}%)`;
}
canvas { border: 1px solid black; }
In other words the first solution draws arrows like this
Where as the second solution draws arrows like this
my simple solution
ctx.beginPath();
ctx.moveTo(ax,ay);
ctx.lineTo(bx,by);
ctx.stroke();
ctx.closePath();
angle=Math.PI+Math.atan2(by-ay,bx-ax);
angle1=angle+Math.PI/6;
angle2=angle-Math.PI/6;
ctx.beginPath();
ctx.moveTo(bx,by);
ctx.arc(bx,by,5,angle1,angle2,true);
ctx.lineTo(bx,by);
ctx.fill();
ctx.closePath();