It works great on desktop, iPhone, and Windows phone but it doesn't scratch on the Android phones. I'm not as familiar with Androids but I'm assuming it has something to do with the javascript. How can i fix this?
https://codepen.io/curthusting/pen/fkCzh
HTML 5:
(function() {
var image = { // back and front images
'back': { 'url': 'http://lorempixel.com/600/400/nature/2', 'img': null },
'front': { 'url': 'http://lorempixel.com/600/400/nature/5', 'img': null }
};
var canvas = { 'temp': null, 'draw': null }; // temp and draw canvases
var mouseDown = false;
/**
* Helper function to get the local coords of an event in an element,
* since offsetX/offsetY are apparently not entirely supported, but
* offsetLeft/offsetTop/pageX/pageY are!
*
* #param elem element in question
* #param ev the event
*/
function getLocalCoords(elem, ev) {
var ox = 0,
oy = 0;
var first;
var pageX, pageY;
// Walk back up the tree to calculate the total page offset of the
// currentTarget element. I can't tell you how happy this makes me.
// Really.
while (elem != null) {
ox += elem.offsetLeft;
oy += elem.offsetTop;
elem = elem.offsetParent;
}
if (ev.hasOwnProperty('changedTouches')) {
first = ev.changedTouches[0];
pageX = first.pageX;
pageY = first.pageY;
} else {
pageX = ev.pageX;
pageY = ev.pageY;
}
return { 'x': pageX - ox, 'y': pageY - oy };
}
/**
* Recomposites the canvases onto the screen
*
* Note that my preferred method (putting the background down, then the
* masked foreground) doesn't seem to work in FF with "source-out"
* compositing mode (it just leaves the destination canvas blank.) I
* like this method because mentally it makes sense to have the
* foreground drawn on top of the background.
*
* Instead, to get the same effect, we draw the whole foreground image,
* and then mask the background (with "source-atop", which FF seems
* happy with) and stamp that on top. The final result is the same, but
* it's a little bit weird since we're stamping the background on the
* foreground.
*
* OPTIMIZATION: This naively redraws the entire canvas, which involves
* four full-size image blits. An optimization would be to track the
* dirty rectangle in scratchLine(), and only redraw that portion (i.e.
* in each drawImage() call, pass the dirty rectangle as well--check out
* the drawImage() documentation for details.) This would scale to
* arbitrary-sized images, whereas in its current form, it will dog out
* if the images are large.
*/
function recompositeCanvases() {
var main = document.getElementById('maincanvas');
var tempctx = canvas.temp.getContext('2d');
var mainctx = main.getContext('2d');
// Step 1: clear the temp
canvas.temp.width = canvas.temp.width; // resizing clears
// Step 2: stamp the draw on the temp (source-over)
tempctx.drawImage(canvas.draw, 0, 0);
/* !!!! this way doesn't work on FF:
// Step 3: stamp the foreground on the temp (!! source-out mode !!)
tempctx.globalCompositeOperation = 'source-out';
tempctx.drawImage(image.front.img, 0, 0);
// Step 4: stamp the background on the display canvas (source-over)
//mainctx.drawImage(image.back.img, 0, 0);
// Step 5: stamp the temp on the display canvas (source-over)
mainctx.drawImage(canvas.temp, 0, 0);
*/
// Step 3: stamp the background on the temp (!! source-atop mode !!)
tempctx.globalCompositeOperation = 'source-atop';
tempctx.drawImage(image.back.img, 0, 0);
// Step 4: stamp the foreground on the display canvas (source-over)
mainctx.drawImage(image.front.img, 0, 0);
// Step 5: stamp the temp on the display canvas (source-over)
mainctx.drawImage(canvas.temp, 0, 0);
}
/**
* Draw a scratch line
*
* #param can the canvas
* #param x,y the coordinates
* #param fresh start a new line if true
*/
function scratchLine(can, x, y, fresh) {
var ctx = can.getContext('2d');
ctx.lineWidth = 50;
ctx.lineCap = ctx.lineJoin = 'round';
ctx.strokeStyle = '#f00'; // can be any opaque color
if (fresh) {
ctx.beginPath();
// this +0.01 hackishly causes Linux Chrome to draw a
// "zero"-length line (a single point), otherwise it doesn't
// draw when the mouse is clicked but not moved:
ctx.moveTo(x + 0.01, y);
}
ctx.lineTo(x, y);
ctx.stroke();
}
/**
* Set up the main canvas and listeners
*/
function setupCanvases() {
var c = document.getElementById('maincanvas');
// set the width and height of the main canvas from the first image
// (assuming both images are the same dimensions)
c.width = image.back.img.width;
c.height = image.back.img.height;
// create the temp and draw canvases, and set their dimensions
// to the same as the main canvas:
canvas.temp = document.createElement('canvas');
canvas.draw = document.createElement('canvas');
canvas.temp.width = canvas.draw.width = c.width;
canvas.temp.height = canvas.draw.height = c.height;
// draw the stuff to start
recompositeCanvases();
/**
* On mouse down, draw a line starting fresh
*/
function mousedown_handler(e) {
var local = getLocalCoords(c, e);
mouseDown = true;
scratchLine(canvas.draw, local.x, local.y, true);
recompositeCanvases();
if (e.cancelable) { e.preventDefault(); }
return false;
};
/**
* On mouse move, if mouse down, draw a line
*
* We do this on the window to smoothly handle mousing outside
* the canvas
*/
function mousemove_handler(e) {
if (!mouseDown) { return true; }
var local = getLocalCoords(c, e);
scratchLine(canvas.draw, local.x, local.y, false);
recompositeCanvases();
if (e.cancelable) { e.preventDefault(); }
return false;
};
/**
* On mouseup. (Listens on window to catch out-of-canvas events.)
*/
function mouseup_handler(e) {
if (mouseDown) {
mouseDown = false;
if (e.cancelable) { e.preventDefault(); }
return false;
}
return true;
};
c.addEventListener('mousedown', mousedown_handler, false);
c.addEventListener('touchstart', mousedown_handler, false);
window.addEventListener('mousemove', mousemove_handler, false);
window.addEventListener('touchmove', mousemove_handler, false);
window.addEventListener('mouseup', mouseup_handler, false);
window.addEventListener('touchend', mouseup_handler, false);
}
/**
* Set up the DOM when loading is complete
*/
function loadingComplete() {
var loading = document.getElementById('loading');
var main = document.getElementById('main');
loading.className = 'hidden';
main.className = '';
}
/**
* Handle loading of needed image resources
*/
function loadImages() {
var loadCount = 0;
var loadTotal = 0;
var loadingIndicator;
function imageLoaded(e) {
loadCount++;
if (loadCount >= loadTotal) {
setupCanvases();
loadingComplete();
}
}
for (k in image)
if (image.hasOwnProperty(k))
loadTotal++;
for (k in image)
if (image.hasOwnProperty(k)) {
image[k].img = document.createElement('img'); // image is global
image[k].img.addEventListener('load', imageLoaded, false);
image[k].img.src = image[k].url;
}
}
/**
* Handle page load
*/
window.addEventListener('load', function() {
var resetButton = document.getElementById('resetbutton');
loadImages();
resetButton.addEventListener('click', function() {
// clear the draw canvas
canvas.draw.width = canvas.draw.width;
recompositeCanvases()
return false;
}, false);
}, false);
})();
* {
margin: 0;
padding: 0;
}
h1 {
width: 100%;
text-align: center;
}
.cell {
display: table-cell;
vertical-align: middle;
text-align: center;
}
.cell canvas {
vertical-align: middle;
}
.canthumb {
border: 0px solid #222;
}
#main {
margin: 0 auto;
width: 600px;
}
#maincanvas {
border: 0px solid #222;
cursor: pointer;
margin: 0 auto;
}
<div id="main">
<h1>"Scratch off" image to reveal a different one</h1>
<div><canvas id="maincanvas"></canvas></div>
<div>
<input id="resetbutton" type="button" value="Reset"></input>
</div>
</div>
Related
So I'm tyring to create a specific pointer trail effect. I'm pasting the code that I'm taking as an example below. The problem is that the trail is dotted, but I'm trying to make a line. I'm trying to recreate the trail that you can see on this site: [1]: https://argor-heraeus.com/
Example of the code that I'm using:
// dots is an array of Dot objects,
// mouse is an object used to track the X and Y position
// of the mouse, set with a mousemove event listener below
var dots = [],
mouse = {
x: 0,
y: 0
};
// The Dot object used to scaffold the dots
var Dot = function() {
this.x = 0;
this.y = 0;
this.node = (function() {
var n = document.createElement("div");
n.className = "trail";
document.body.appendChild(n);
return n;
}());
};
// The Dot.prototype.draw() method sets the position of
// the object's <div> node
Dot.prototype.draw = function() {
this.node.style.left = this.x + "px";
this.node.style.top = this.y + "px";
};
// Creates the Dot objects, populates the dots array
for (var i = 0; i < 12; i++) {
var d = new Dot();
dots.push(d);
}
// This is the screen redraw function
function draw() {
// Make sure the mouse position is set everytime
// draw() is called.
var x = mouse.x,
y = mouse.y;
// This loop is where all the 90s magic happens
dots.forEach(function(dot, index, dots) {
var nextDot = dots[index + 1] || dots[0];
dot.x = x;
dot.y = y;
dot.draw();
x += (nextDot.x - dot.x) * .6;
y += (nextDot.y - dot.y) * .6;
});
}
addEventListener("mousemove", function(event) {
//event.preventDefault();
mouse.x = event.pageX;
mouse.y = event.pageY;
});
// animate() calls draw() then recursively calls itself
// everytime the screen repaints via requestAnimationFrame().
function animate() {
draw();
requestAnimationFrame(animate);
}
// And get it started by calling animate().
animate();
body {
background-color: black;
}
.trail {
/* className for the trail elements */
position: absolute;
height: 6px;
width: 6px;
border-radius: 3px;
background: gold;
}
I'm playing with margins (left,right) , with width, but I can't "stick them" together. Anyone got an idea how I could make a clean line?
So I changed this in the given code:
x += (nextDot.x - dot.x) * **0.05**;
y += (nextDot.y - dot.y) * **0.05**;
And added more dots, 80, to be precise.
I also added a more smooth transition with this CSS rules
-webkit-transition:90ms;
transition:90ms;
pointer-events: none;
I have square that follows my cursor.
Its border top is red to see if the rotation is right.
I'm trying to rotate it depending on mouse movement angle. Like if mouse goes 45deg top right then square must rotate by 45deg.
The problem is that when I move my mouse slowly the square starts to rotate like crazy. But if I move my mouse fast enough square rotates pretty smooth.
Actually it's just a part of my task that I'm trying to accomplish. My whole task is to make custom circle cursor that stretches when mouse moving. The idea I'm trying to implement:
rotate circle by mouse movement angle and then just scaleX it to make stretching effect. But I cannot do it because of problem I described above. I need my follower to rotate smoothly when mouse speed is slow.
class Cursor {
constructor() {
this.prevX = null;
this.prevY = null;
this.curX = null;
this.curY = null;
this.angle = null;
this.container = document.querySelector(".cursor");
this.follower = this.container.querySelector(".cursor-follower");
document.addEventListener("mousemove", (event) => {
this.curX = event.clientX;
this.curY = event.clientY;
});
this.position();
}
position(timestamp) {
this.follower.style.top = `${this.curY}px`;
this.follower.style.left = `${this.curX}px`;
this.angle = Math.atan2(this.curY - this.prevY, this.curX - this.prevX) * 180/Math.PI;
console.log(this.angle + 90);
this.follower.style.transform = `rotateZ(${this.angle + 90}deg)`;
this.prevX = this.curX;
this.prevY = this.curY;
requestAnimationFrame(this.position.bind(this));
}
}
const cursor = new Cursor();
.cursor-follower {
position: fixed;
top: 0;
left: 0;
z-index: 9999;
pointer-events: none;
user-select: none;
width: 76px;
height: 76px;
margin: -38px;
border: 1.5px solid #000;
border-top: 1.5px solid red;
}
<div class="cursor">
<div class="cursor-follower"></div>
</div>
Following the cursor tangent smoothly isn't as simple as it first feels. In modern browsers mousemove event fires nearby at the frame rate (typically 60 FPS). When the mouse is moving slowly, the cursor moves only a pixel or two between the events. When calculating the angle, vertical + horizontal move of 1px resolves to 45deg. Then there's another problem, the event firing rate is not consistent, during the mouse is moving, event firing rate can drop to 30 FPS or even to 24 FPS, which actually helps to get more accurate angle, but makes the scale calculations heavily inaccurate (your real task seems to need scale calculations too).
One solution is to use CSS Transitions to make the animation smoother. However, adding a transition makes the angle calculations much more complex, because the jumps between negative and positive angles Math.atan2 returns when crossing PI will become visible when using transition.
Here's a sample code of how to use transition to make the cursor follower smoother.
class Follower {
// Default options
threshold = 4;
smoothness = 10;
stretchRate = 100;
stretchMax = 100;
stretchSlow = 100;
baseAngle = Math.PI / 2;
// Class initialization
initialized = false;
// Listens mousemove event
static moveCursor (e) {
if (Follower.active) {
Follower.prototype.crsrMove.call(Follower.active, e);
}
}
static active = null;
// Adds/removes mousemove listener
static init () {
if (this.initialized) {
document.removeEventListener('mousemove', this.moveCursor);
if (this.active) {
this.active.cursor.classList.add('hidden');
}
} else {
document.addEventListener('mousemove', this.moveCursor);
}
this.initialized = !this.initialized;
}
// Base values of instances
x = -1000;
y = -1000;
angle = 0;
restoreTimer = -1;
stamp = 0;
speed = [0];
// Prototype properties
constructor (selector) {
this.cursor = document.querySelector(selector);
this.restore = this.restore.bind(this);
}
// Activates a new cursor
activate (options = {}) {
// Remove the old cursor
if (Follower.active) {
Follower.active.cursor.classList.add('hidden');
Follower.active.cursor.classList.remove('cursor', 'transitioned');
}
// Set the new cursor
Object.assign(this, options);
this.setCss = this.cursor.style.setProperty.bind(this.cursor.style);
this.cursor.classList.remove('hidden');
this.cHW = this.cursor.offsetWidth / 2;
this.cHH = this.cursor.offsetHeight / 2;
this.setCss('--smoothness', this.smoothness / 100 + 's');
this.cursor.classList.add('cursor');
setTimeout(() => this.cursor.classList.add('transitioned'), 0); // Snap to the current angle
this.crsrMove({
clientX: this.x,
clientY: this.y
});
Follower.active = this;
return this;
}
// Moves the cursor with effects
crsrMove (e) {
clearTimeout(this.restoreTimer); // Cancel reset timer
const PI = Math.PI,
pi = PI / 2,
x = e.clientX,
y = e.clientY,
dX = x - this.x,
dY = y - this.y,
dist = Math.hypot(dX, dY);
let rad = this.angle + this.baseAngle,
dTime = e.timeStamp - this.stamp,
len = this.speed.length,
sSum = this.speed.reduce((a, s) => a += s),
speed = dTime
? ((1000 / dTime) * dist + sSum) / len
: this.speed[len - 1], // Old speed when dTime = 0
scale = Math.min(
this.stretchMax / 100,
Math.max(speed / (500 - this.stretchRate || 1),
this.stretchSlow / 100
)
);
// Update base values and rotation angle
if (isNaN(dTime)) {
scale = this.scale;
} // Prevents a snap of a new cursor
if (len > 5) {
this.speed.length = 1;
}
// Update angle only when mouse has moved enough from the previous update
if (dist > this.threshold) {
let angle = Math.atan2(dY, dX),
dAngle = angle - this.angle,
adAngle = Math.abs(dAngle),
cw = 0;
// Smoothen small angles
if (adAngle < PI / 90) {
angle += dAngle * 0.5;
}
// Crossing ±PI angles
if (adAngle >= 3 * pi) {
cw = -Math.sign(dAngle) * Math.sign(dX); // Rotation direction: -1 = CW, 1 = CCW
angle += cw * 2 * PI - dAngle; // Restores the current position with negated angle
// Update transform matrix without transition & rendering
this.cursor.classList.remove('transitioned');
this.setCss('--angle', `${angle + this.baseAngle}rad`);
this.cursor.offsetWidth; // Matrix isn't updated without layout recalculation
this.cursor.classList.add('transitioned');
adAngle = 0; // The angle was handled, prevent further adjusts
}
// Orthogonal mouse turns
if (adAngle >= pi && adAngle < 3 * pi) {
this.cursor.classList.remove('transitioned');
setTimeout(() => this.cursor.classList.add('transitioned'), 0);
}
rad = angle + this.baseAngle;
this.x = x;
this.y = y;
this.angle = angle;
}
this.scale = scale;
this.stamp = e.timeStamp;
this.speed.push(speed);
// Transform the cursor
this.setCss('--angle', `${rad}rad`);
this.setCss('--scale', `${scale}`);
this.setCss('--tleft', `${x - this.cHW}px`);
this.setCss('--ttop', `${y - this.cHH}px`);
// Reset the cursor when mouse stops
this.restoreTimer = setTimeout(this.restore, this.smoothness + 100, x, y);
}
// Returns the position parameters of the cursor
position () {
const {x, y, angle, scale, speed} = this;
return {x, y, angle, scale, speed};
}
// Restores the cursor
restore (x, y) {
this.state = 0;
this.setCss('--scale', 1);
this.scale = 1;
this.speed = [0];
this.x = x;
this.y = y;
}
}
Follower.init();
const crsr = new Follower('.crsr').activate();
body {
margin: 0px;
}
.crsr {
width: 76px;
height: 76px;
border: 2px solid #000;
border-radius: 0%;
text-align: center;
font-size: 20px;
}
.cursor {
position: fixed;
cursor: default;
user-select: none;
left: var(--tleft);
top: var(--ttop);
transform: rotate(var(--angle)) scaleY(var(--scale));
}
.transitioned {
transition: transform var(--smoothness) linear;
}
.hidden {
display: none;
}
<div class="crsr hidden">A</div>
The basic idea of the code is to wait until the mouse has moved enough pixels (threshold) to calculate the angle. The "mad circle" effect is tackled by setting the angle to the same position, but at the negated angle when crossing PI. This change is made invisibly between the renderings.
CSS variables are used for the actual values in transform, this allows to change a single parameter of the transform functions at the time, you don't have to rewrite the entire rule. setCss method is just syntactic sugar, it makes the code a little bit shorter.
The current parameters are showing a rectangle follower as it is in your question. Setting ex. stretchMax = 300 and stretchSlow = 125 and adding 50% border radius to CSS might be near to what you finally need. stretchRate defines the stretch related to the speed of the mouse. If the slow motion is still not smooth enough for your purposes, you can create a better algorithm to // Smoothen small angles section (in crsrMove method). You can play with the parameters at jsFiddle.
Try like this
class Cursor {
constructor() {
this.prevX = null;
this.prevY = null;
this.curX = null;
this.curY = null;
this.angle = null;
this.container = document.querySelector(".cursor");
this.follower = this.container.querySelector(".cursor-follower");
document.addEventListener("mousemove", (event) => {
this.curX = event.clientX;
this.curY = event.clientY;
});
this.position();
}
position(timestamp) {
this.follower.style.top = `${this.curY}px`;
this.follower.style.left = `${this.curX}px`;
if (this.curY !== this.prevY && this.curX !== this.prevX) {
this.angle = Math.atan2(this.curY - this.prevY, this.curX - this.prevX) * 180/Math.PI;
}
console.log(this.angle + 90);
this.follower.style.transform = `rotateZ(${this.angle + 90}deg)`;
this.prevX = this.curX;
this.prevY = this.curY;
requestAnimationFrame(this.position.bind(this));
}
}
const cursor = new Cursor();
I want to build a UI with a joystick, To implement the joystick, I used the JoyStick js library: https://github.com/bobboteck/JoyStick
My code works in Firefox and Chrome on Windows 10, but on Android the joystick doesn't appear with the following error messages:
(index):1889 crbug/1173575, non-JS module files deprecated.
(anonymous) # (index):1889
chromewebdata/:1 Not allowed to load local resource: content://0#media/external/file/10769
My code:
controller.html
<!DOCTYPE html>
<html>
<head>
<title>drone controller</title>
<meta charset="utf-8">
<script src="joy.js"></script>
</head>
<body>
<!-- Example of FIXED or ABSOLUTE position -->
<div id="container" style="width:200px;height:200px;margin:50px;position:fixed;bottom:30px;left:500px;">
</div>
<div style="position:fixed;bottom:125px;left:750px;">
Posizione X:<input id="joy3PosizioneX" type="text"><br>
Posizione Y:<input id="joy3PosizioneY" type="text"><br>
Direzione:<input id="joy3Direzione" type="text"><br>
X :<input id="joy3X" type="text"><br>
Y :<input id="joy3Y" type="text">
</div>
<script>
var joy3 = new JoyStick('container');
// var joy3Param = { "title": "joystick3" };
// var Joy3 = new JoyStick('joy3Div', joy3Param);
var joy3IinputPosX = document.getElementById("joy3PosizioneX");
var joy3InputPosY = document.getElementById("joy3PosizioneY");
var joy3Direzione = document.getElementById("joy3Direzione");
var joy3X = document.getElementById("joy3X");
var joy3Y = document.getElementById("joy3Y");
setInterval(function(){ joy3IinputPosX.value=joy3.GetPosX(); }, 50);
setInterval(function(){ joy3InputPosY.value=joy3.GetPosY(); }, 50);
setInterval(function(){ joy3Direzione.value=joy3.GetDir(); }, 50);
setInterval(function(){ joy3X.value=joy3.GetX(); }, 50);
setInterval(function(){ joy3Y.value=joy3.GetY(); }, 50);
</script>
</body>
</html>
joy.js unmodified from github
var JoyStick = (function(container, parameters)
{
parameters = parameters || {};
var title = (typeof parameters.title === "undefined" ? "joystick" : parameters.title),
width = (typeof parameters.width === "undefined" ? 0 : parameters.width),
height = (typeof parameters.height === "undefined" ? 0 : parameters.height),
internalFillColor = (typeof parameters.internalFillColor === "undefined" ? "#00AA00" : parameters.internalFillColor),
internalLineWidth = (typeof parameters.internalLineWidth === "undefined" ? 2 : parameters.internalLineWidth),
internalStrokeColor = (typeof parameters.internalStrokeColor === "undefined" ? "#003300" : parameters.internalStrokeColor),
externalLineWidth = (typeof parameters.externalLineWidth === "undefined" ? 2 : parameters.externalLineWidth),
externalStrokeColor = (typeof parameters.externalStrokeColor === "undefined" ? "#008000" : parameters.externalStrokeColor),
autoReturnToCenter = (typeof parameters.autoReturnToCenter === "undefined" ? true : parameters.autoReturnToCenter);
// Create Canvas element and add it in the Container object
var objContainer = document.getElementById(container);
var canvas = document.createElement("canvas");
canvas.id = title;
if(width === 0) { width = objContainer.clientWidth; }
if(height === 0) { height = objContainer.clientHeight; }
canvas.width = width;
canvas.height = height;
objContainer.appendChild(canvas);
var context=canvas.getContext("2d");
var pressed = 0; // Bool - 1=Yes - 0=No
var circumference = 2 * Math.PI;
var internalRadius = (canvas.width-((canvas.width/2)+10))/2;
var maxMoveStick = internalRadius + 5;
var externalRadius = internalRadius + 30;
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var directionHorizontalLimitPos = canvas.width / 10;
var directionHorizontalLimitNeg = directionHorizontalLimitPos * -1;
var directionVerticalLimitPos = canvas.height / 10;
var directionVerticalLimitNeg = directionVerticalLimitPos * -1;
// Used to save current position of stick
var movedX=centerX;
var movedY=centerY;
// Check if the device support the touch or not
if("ontouchstart" in document.documentElement)
{
canvas.addEventListener("touchstart", onTouchStart, false);
canvas.addEventListener("touchmove", onTouchMove, false);
canvas.addEventListener("touchend", onTouchEnd, false);
}
else
{
canvas.addEventListener("mousedown", onMouseDown, false);
canvas.addEventListener("mousemove", onMouseMove, false);
canvas.addEventListener("mouseup", onMouseUp, false);
}
// Draw the object
drawExternal();
drawInternal();
/******************************************************
* Private methods
*****************************************************/
/**
* #desc Draw the external circle used as reference position
*/
function drawExternal()
{
context.beginPath();
context.arc(centerX, centerY, externalRadius, 0, circumference, false);
context.lineWidth = externalLineWidth;
context.strokeStyle = externalStrokeColor;
context.stroke();
}
/**
* #desc Draw the internal stick in the current position the user have moved it
*/
function drawInternal()
{
context.beginPath();
if(movedX<internalRadius) { movedX=maxMoveStick; }
if((movedX+internalRadius) > canvas.width) { movedX = canvas.width-(maxMoveStick); }
if(movedY<internalRadius) { movedY=maxMoveStick; }
if((movedY+internalRadius) > canvas.height) { movedY = canvas.height-(maxMoveStick); }
context.arc(movedX, movedY, internalRadius, 0, circumference, false);
// create radial gradient
var grd = context.createRadialGradient(centerX, centerY, 5, centerX, centerY, 200);
// Light color
grd.addColorStop(0, internalFillColor);
// Dark color
grd.addColorStop(1, internalStrokeColor);
context.fillStyle = grd;
context.fill();
context.lineWidth = internalLineWidth;
context.strokeStyle = internalStrokeColor;
context.stroke();
}
/**
* #desc Events for manage touch
*/
function onTouchStart(event)
{
pressed = 1;
}
function onTouchMove(event)
{
// Prevent the browser from doing its default thing (scroll, zoom)
event.preventDefault();
if(pressed === 1 && event.targetTouches[0].target === canvas)
{
movedX = event.targetTouches[0].pageX;
movedY = event.targetTouches[0].pageY;
// Manage offset
if(canvas.offsetParent.tagName.toUpperCase() === "BODY")
{
movedX -= canvas.offsetLeft;
movedY -= canvas.offsetTop;
}
else
{
movedX -= canvas.offsetParent.offsetLeft;
movedY -= canvas.offsetParent.offsetTop;
}
// Delete canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// Redraw object
drawExternal();
drawInternal();
}
}
function onTouchEnd(event)
{
pressed = 0;
// If required reset position store variable
if(autoReturnToCenter)
{
movedX = centerX;
movedY = centerY;
}
// Delete canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// Redraw object
drawExternal();
drawInternal();
//canvas.unbind('touchmove');
}
/**
* #desc Events for manage mouse
*/
function onMouseDown(event)
{
pressed = 1;
}
function onMouseMove(event)
{
if(pressed === 1)
{
movedX = event.pageX;
movedY = event.pageY;
// Manage offset
if(canvas.offsetParent.tagName.toUpperCase() === "BODY")
{
movedX -= canvas.offsetLeft;
movedY -= canvas.offsetTop;
}
else
{
movedX -= canvas.offsetParent.offsetLeft;
movedY -= canvas.offsetParent.offsetTop;
}
// Delete canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// Redraw object
drawExternal();
drawInternal();
}
}
function onMouseUp(event)
{
pressed = 0;
// If required reset position store variable
if(autoReturnToCenter)
{
movedX = centerX;
movedY = centerY;
}
// Delete canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// Redraw object
drawExternal();
drawInternal();
//canvas.unbind('mousemove');
}
/******************************************************
* Public methods
*****************************************************/
/**
* #desc The width of canvas
* #return Number of pixel width
*/
this.GetWidth = function ()
{
return canvas.width;
};
/**
* #desc The height of canvas
* #return Number of pixel height
*/
this.GetHeight = function ()
{
return canvas.height;
};
/**
* #desc The X position of the cursor relative to the canvas that contains it and to its dimensions
* #return Number that indicate relative position
*/
this.GetPosX = function ()
{
return movedX;
};
/**
* #desc The Y position of the cursor relative to the canvas that contains it and to its dimensions
* #return Number that indicate relative position
*/
this.GetPosY = function ()
{
return movedY;
};
/**
* #desc Normalizzed value of X move of stick
* #return Integer from -100 to +100
*/
this.GetX = function ()
{
return (100*((movedX - centerX)/maxMoveStick)).toFixed();
};
/**
* #desc Normalizzed value of Y move of stick
* #return Integer from -100 to +100
*/
this.GetY = function ()
{
return ((100*((movedY - centerY)/maxMoveStick))*-1).toFixed();
};
/**
* #desc Get the direction of the cursor as a string that indicates the cardinal points where this is oriented
* #return String of cardinal point N, NE, E, SE, S, SW, W, NW and C when it is placed in the center
*/
this.GetDir = function()
{
var result = "";
var orizontal = movedX - centerX;
var vertical = movedY - centerY;
if(vertical >= directionVerticalLimitNeg && vertical <= directionVerticalLimitPos)
{
result = "C";
}
if(vertical < directionVerticalLimitNeg)
{
result = "N";
}
if(vertical > directionVerticalLimitPos)
{
result = "S";
}
if(orizontal < directionHorizontalLimitNeg)
{
if(result === "C")
{
result = "W";
}
else
{
result += "W";
}
}
if(orizontal > directionHorizontalLimitPos)
{
if(result === "C")
{
result = "E";
}
else
{
result += "E";
}
}
return result;
};
});
Thanks for your help!
I take it you have copied the files to the phone's local storage, and are trying to open them from there in Chrome?
I suspect it not working is intentional. Access to local files can be quite restricted in web browsers in general (since it doesn't really work together with the origin-based security model), and access to local storage on Android is an equally complicated subject.
If you're planning to serve this on the web, and just want to test whether it works in Chrome on Android, the path of least resistance will probably be to just serve it over HTTP. How to do that is probably outside the scope of the question, but for example (as described near the end of this page) Python's http.server module can be used as a simple command-line web server for testing purposes, assuming you have a computer and a phone in the same local network (or use adb port forwarding).
Edit: I've just tested your files using the above method (with python3 -m http.server), and it does show the green joystick on Chrome 92 on Android.
Alternatively, if your end goal is to package this into an Android app, there are specific APIs (such as the WebViewAssetLoader) that allow you to serve the local HTTP and JS assets in a way that will not run into problems like this.
This gamepad API is relatively new and still now experimental. To my knowledge, only latest verson of Crome for android supports it. Update your crome browser on android and try. This should work.
I'm working on a script which is supposed to do the following. You lock your mouse to a canvas. It will show you an "artificial" cursor instead that you can also move by using your mouse. Under this cursor, you will have a circle which also moves with the mouse.
All of this worked perfectly fine with my script which was until I added another nice feature: I want to have an Arrow in the middle of the canvas which stays there, exact same size, but rotates according to your cursor movement. To give you an idea what I'm talking about, I made these example graphs (don't worry about dimensions and colour).
https://i.stack.imgur.com/poO6n.jpg
https://i.stack.imgur.com/twXhY.jpg
https://i.stack.imgur.com/RFFBe.jpg
I did some calculations to do this, implemented them, hoped for the best, but it doesn't work. I thought when it works, it will be a cool feature for everyone to have on this site. But so far I didn't see where my mistake is. If you have a clue, I'm absolutely grateful for every answer.
Many thanks!
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>ArrowSpin</title>
<style>
html, body {
margin: 0;
padding: 0;
}
html {
font-family: sans-serif;
}
canvas {
display: block;
margin: 0 auto;
border: 1px solid black;
}
.information {
width: 640px;
margin: 0 auto 50px;
}
#tracker {
position: absolute;
top: 0;
right: 10px;
background-color: white;
}
h1 {
font-size: 200%;
}
</style>
</head>
<body>
<div class="information">
<img id="mousecursor" hidden="true" width="13" height="20.5" src="mousecursor.png" alt="Cursor">
<p id="demo" style="color: black" oncl></p>
<p id="Message" style="color: black" oncl></p>
<canvas id="myCanvas" width="640" height="360">
Your browser does not support HTML5 canvas
</canvas>
<div id="tracker"></div>
</div>
<script>
try {
// helper functions
const RADIUS = 20;
// this image is you mousecursor
var img = document.getElementById("mousecursor");
// degree to radians
function degToRad(degrees) {
var result = Math.PI / 180 * degrees;
return result;
}
// generate a random number, later on, mouse cursor should start at random position, now unused
function generateRandomNumber() {
var minangle = 0;
var maxangle = 2*Math.PI;
randangle = Math.random() * (maxangle- minangle) + minangle;
return randangle;
};
//this function draws the actual arrow
function drawArrow(fromx, fromy, tox, toy, colourarrow){
//variables to be used when creating the arrow
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var headlen = 3;
var angle = Math.atan2(toy-fromy,tox-fromx);
//starting path of the arrow from the start square to the end square and drawing the stroke
ctx.beginPath();
ctx.moveTo(fromx, fromy);
ctx.lineTo(tox, toy);
ctx.strokeStyle = colourarrow;
ctx.lineWidth = 20;
ctx.stroke();
//starting a new path from the head of the arrow to one of the sides of the point
ctx.beginPath();
ctx.moveTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
//path from the side point of the arrow, to the other side point
ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
//path from the side point back to the tip of the arrow, and then again to the opposite side point
ctx.lineTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
//draws the paths created above
ctx.strokeStyle = colourarrow;
ctx.lineWidth = 20;
ctx.stroke();
ctx.fillStyle = colourarrow
ctx.fill();
}
// this function calculates the current angle of the cursor from the exact middle of the canvas (x0,y0) by using two simple assumptions which are a) radius=sqrt(sqr(xfrom)+sqr(y)) b) x=x0+radius*cos(alpha) <=> alpha=arccos((x-x0)/radius)
function CursorAngle() {
var currentrad=Math.sqrt([Math.pow(x-canvas.width/2)+Math.pow(y+canvas.height)]);
var currentangle=Math.acos([(x-canvas.width/2)/currentrad]);
return currentangle
}
//in this function I use the just calculated cursor angle to now calculate where my arrow shall begin and end, again I use x=x0+radius*cos(alpha) and y=y0+radius*sin(alpha). In this case I always want my arrow to have a radius of 50 and I always want it to be drawn in the center of the canvas.
function ProbeAngle(alpha) {
var x1 = canvas.width/2+50*cos(alpha)
var y1 = canvas.width/2+50*sin(alpha)
var x2 = canvas.width/2+50*cos(alpha+Math.PI)
var y2 = canvas.width/2+50*sin(alpha+Math.PI)
return [x1; y1; x2; y2]
}
// setup of the canvas
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var x = canvas.width/2;
var y = canvas.height/2;
//refresh the canvas
function canvasDraw() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#f00";
ctx.beginPath();
ctx.arc(x, y, RADIUS, 0, degToRad(360), true);
ctx.fill();
ctx.drawImage(img, x, y);
}
canvasDraw();
// pointer lock object forking for cross browser
canvas.requestPointerLock = canvas.requestPointerLock ||
canvas.mozRequestPointerLock;
document.exitPointerLock = document.exitPointerLock ||
document.mozExitPointerLock;
canvas.onclick = function() {
canvas.requestPointerLock();
canvasDraw();
};
// pointer lock event listeners
// Hook pointer lock state change events for different browsers
document.addEventListener('pointerlockchange', lockChangeAlert, false);
document.addEventListener('mozpointerlockchange', lockChangeAlert, false);
function lockChangeAlert() {
if (document.pointerLockElement === canvas ||
document.mozPointerLockElement === canvas) {
console.log('The pointer lock status is now locked');
document.addEventListener("mousemove", updatePosition, false);
} else {
console.log('The pointer lock status is now unlocked');
document.removeEventListener("mousemove", updatePosition, false);
}
}
//tracker shows x and y coordinates of "pseudo" cursor
var tracker = document.getElementById('tracker');
//border protection for our image not to move out of the canvas
var animation;
function updatePosition(e) {
x += e.movementX;
y += e.movementY;
if (x > canvas.width) {
x = canvas.width;
}
if (y > canvas.height) {
y = canvas.height;
}
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
tracker.textContent = "X position: " + x + ", Y position: " + y;
if (!animation) {
animation = requestAnimationFrame(function() {
animation = null;
canvasDraw();
//receive the ProbeCoords by using the functions CursorAngle and ProbeAngle and draw it!
var ProbeCoord = ProbeAngle(CursorAngle());
drawArrow(ProbeCoord[0],ProbeCoord[1],ProbeCoord[2],ProbeCoord[3],'white')
});
}
}
}
catch(err) {
document.getElementById("demo").innerHTML = err.message;
}
document.getElementById("Message").innerHTML = "potential Errorcode above";
</script>
</body>
</html>
Have you tried using Fabric JS? In the linked example you can click an object and a handle appears at the top. After that you can click handle and it will follow the mouse. I'm suggesting this because most likely there is a way to change the click event to a hover event and then get the handle to follow the mouse.
Hello i create program like a paint on HTML5 canvas. I have problem i need create few tools drawing and zoom. I don't have idea how to create zoom without delay. Drawing example: http://jsfiddle.net/x5rrvcr0/
How i can zooming my drawings?
drawing code:
<style>
canvas {
background-color: #CECECE;
}
html, body {
background-color: #FFFFFF;
}
</style>
<script>
$(document).ready(function () {
var paintCanvas = document.getElementById("paintCanvas");
var paintCtx = paintCanvas.getContext("2d");
var size = 500;
paintCanvas.width = size;
paintCanvas.height = size;
var draw = false;
var prevMouseX = 0;
var prevMouseY = 0;
function getMousePos(canvas, evt) {
evt = evt.originalEvent || window.event || evt;
var rect = canvas.getBoundingClientRect();
if (evt.clientX !== undefined && evt.clientY !== undefined) {
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
}
$("#paintCanvas").on("mousedown", function(e) {
draw = true;
var coords = getMousePos(paintCanvas);
prevMouseX = coords.x;
prevMouseY = coords.y;
});
$("#paintCanvas").on("mousemove", function(e) {
if(draw) {
var coords = getMousePos(paintCanvas, e);
paintCtx.beginPath();
paintCtx.lineWidth = 10;
paintCtx.strokeStyle = "#000000";
paintCtx.moveTo(prevMouseX, prevMouseY);
paintCtx.lineTo(coords.x, coords.y);
paintCtx.stroke();
prevMouseX = coords.x;
prevMouseY = coords.y;
}
});
$("#paintCanvas").on("mouseup", function(e) {
draw = false;
});
});
</script>
<body>
<canvas id="paintCanvas"></canvas>
</body>
If you want to keep the pixelated effect in the zoom, you need to draw on a temp canvas, then only after copy that temp canvas to the main screen.
You no longer need to zoom in the temp canvas, just draw on 1:1 scale always. When copying to the view canvas, then you apply the zoom (and maybe translate) that you want.
Keep in mind that drawings are anti-aliased, so you when zooming you will see some shades of grey when drawing in black, for instance.
I kept the recording code of #FurqanZafar since it is a good idea to record things in case you want to perform undo : in that case just delete the last record entry and redraw everything.
http://jsfiddle.net/gamealchemist/x5rrvcr0/4/
function updatePaintCanvas() {
paintContext.clearRect(0, 0, paintContext.canvas.width, paintContext.canvas.height);
paintContext.save();
paintContext.translate(cvSize * 0.5, cvSize * 0.5);
paintContext.scale(scale, scale);
paintContext.drawImage(drawCanvas, -cvSize * 0.5, -cvSize * 0.5);
paintContext.restore();
}
Heres the updated fiddle: http://jsfiddle.net/x5rrvcr0/2/ with basic zooming functionality
If you draw multiple paths on mouse move then your sketch will appear broken or disconnected, instead you should only stroke a single path until "mouseup" event.
You can then store these paths in an array and later redraw them at different zoom levels:
function zoom(context, paths, styles, scale) {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.save();
applyStyles(context, styles);
scaleFromCenter(context, scale);
for (var i = 0; i < paths.length; i++) {
context.beginPath();
context.moveTo(paths[i][0].x, paths[i][0].y);
for (var j = 1; j < paths[i].length; j++)
context.lineTo(paths[i][j].x, paths[i][j].y);
context.stroke();
}
context.restore();
};