I'm having an issue with a HTML5 canvas idea. It's basically a game, whereby the player is always centered in the canvas - this means that for 'movement' the rest of the scene must move instead. I have this setup, but I also wanted to have a clickable grid (50x50 px) on top of the scene (as if the game's elements are made up of blocks).
For drawing the mouse box I have the following code:
// Draw mouse box
if(game.mouse.o){
var mouseBlockX = (Math.floor(game.mouse.x / 50) * 50) + (game.view.x % 50);
// Previous attempt:
// var mouseBlockX = (Math.floor((game.mouse.x - ( 0.5 * game.view.x % 50)) / 50) * 50) + (game.view.x % 50);
var mouseBlockY = (Math.floor((game.mouse.y - ( 0.5 * game.view.y % 50)) / 50) * 50) + (game.view.y % 50);
ctx.fillText('Block: ' + mouseBlockX.toString() + '/' + mouseBlockY.toString(), 5, 400);
ctx.fillText(Math.abs(game.view.x) % 50, 5, 410);
ctx.strokeStyle = '#DDDDFF';
ctx.strokeWidth = 1;
ctx.strokeRect(mouseBlockX, mouseBlockY, 50, 50);
}
Essentially, my main issue here is that the following line doesn't work properly:
(Math.floor((game.mouse.x - ( 0.5 * game.view.x % 50)) / 50) * 50) + (game.view.x % 50)
...and I've tried to find a correct version, but it's beating me at the minute. What would be the correct formula so that the mouse's box follows the mouse, but also compensates for the view moving? Have I made it too complicated so I'm missing the answer?
JSFiddle: http://jsfiddle.net/y14ftv0o/
UPDATE: changed variables to be clearer. To try and explain in a better way: when the view moves, the 'grid' doesn't properly stick to its original position. When the view moves, the grid should look like it is fixed to the original objects on the scene (essentially so the user thinks they're 'clicking' on things in the scene on a grid).
Related
My Three.js project uses and OrthographicCamera and OrthographicTrackBallControls for zoom/pan. I'm trying to add functionality to zoom to the cursor position with no luck. First things first, here's how I'm getting mouse position:
var mX = ((event.clientX - offset.left) / renderer.domElement.clientWidth) * 2 - 1;
var mY = -((event.clientY - offset.top) / renderer.domElement.clientHeight) * 2 + 1;
var vector = new THREE.Vector3(mX, mY, 0.5);
vector.unproject(camera);
vector.sub(camera.position);
Through looking on StackOverflow, there seems to be a lot of information on how to do this with PerspectiveCamera, but these methods don't work with OrthographicCamera. I was able to find this example:
https://htmlpreview.github.io/?https://github.com/w3dot0/three.js/blob/973bf1d40ef552dbf19c19654a79f70e2882563d/examples/misc_controls_zoom_to_mouse.html
Which does precisely what I am trying to accomplish, but the code that achieves this is hidden, though I am able to discern that the camera position is being changed.
Another SO question which is similar suggests changing camera.left, camera.right, camera.top and camera.bottom, but I have had no luck with this approach. This approach seems like a possibility, but I dont understand the calculations necessary to get the correct left, right, top and bottom values.
So the way I see it I have two possibilities:
Change camera's left/right/top/bottom to get the correct view rectangle.
Change camera position.
But I don't know how to get the values I need to accomplish either, let alone which is the better approach.
UPDATE 11/16/2018:
I've updated my function to this ( based on https://github.com/w3dot0/three.js/blob/973bf1d40ef552dbf19c19654a79f70e2882563d/examples/misc_controls_zoom_to_mouse.html):
zoomDirection = new THREE.Vector3();
function mousewheel(event) {
event.preventDefault();
var amount = event.deltaY / 100;
var zoom = camera.zoom - amount;
var offset = el.offset();
;
var mX = amount > 0 ? 0 : ((event.clientX - offset.left) / renderer.domElement.clientWidth) * 2 - 1;
var mY = amount > 0 ? 0 : -((event.clientY - offset.top) / renderer.domElement.clientHeight) * 2 + 1;
zoomDirection.set(mX, mY, 0.001)
.unproject(camera)
.sub(camera.position)
.multiplyScalar(amount / zoom);
camera.position.subVectors(camera.position, zoomDirection);
orthographictrackBallControls.target.subVectors(orthographictrackBallControls.target, webGl.zoomDirection);
camera.zoom = zoom;
camera.updateProjectionMatrix();
}
This seems to work at first: the camera zooms into the mouse point, but then the camera starts to "jump" around after a bit of zooming, with the mesh no longer visible on screen.
Something that might help: I have an axis helper in the screen as well that "flips" when it stops working as expected. When the scene is loaded, the X-axis helper point due left, but when I get to the point where the camera jumps and I no longer see the mesh, the X-axis helper flips to point due right.
Also, if I zoom OUT first, I can zoom in further before the mesh disappears. I'm not sure what this all adds up to but I would appreciate any help.
First week back after New Year and it's taken too long to fix this. Six sides of A4 covered with linear algebra results in
if ( factor !== 1.0 && factor > 0.0 ) {
const mX = (event.offsetX / event.target.width ) * 2 - 1;
const mY = -(event.offsetY / event.target.height) * 2 + 1;
const vector1 = new THREE.Vector3(mX, mY, 0);
const vector2 = new THREE.Vector3(0, 0, 0);
vector1.unproject(this.camera);
vector2.unproject(this.camera);
vector1.subVectors(vector1, vector2);
this.camera.zoom /= factor;
vector1.multiplyScalar(factor - 1.0);
this.camera.position.subVectors(this.camera.position, vector1);
this.controls.target.subVectors(this.controls.target, vector1);
this.camera.updateProjectionMatrix();
this.camera.updateMatrix();
}
Note the different calculation of mX, mY so that it is valid for a viewport.
Implementing the D3-library with its zoom function may seem like a good idea for this case. But giving up the three-controls is in a lot of cases not a deal.
If you want a zoom-behavior like in Google Maps, the following code could be helpful:
const cameraPosition = camera.position.clone();
// my camera.zoom starts with 0.2
if (zoomOld !== 0.2) {
const xNew = this.curserVector.x + (((cameraPosition.x - this.curserVector.x) * camera.zoom) /zoomOld);
const yNew = this.curserVector.y + (((cameraPosition.y - this.curserVector.y) * camera.zoom) /zoomOld);
const diffX = cameraPosition.x - xNew;
const diffY = cameraPosition.y - yNew;
camera.position.x += diffX;
camera.position.y += diffY;
controls.target.x += diffX;
controls.target.y += diffY;
}
zoomOld = camera.zoom;
Your other problem could be caused by the frustum. But I don't know, I'm still a newbie with Three xD
Here is my problem, i created a web application where you need to load a file (image) then it appears in a canvas after a resizing in order not to excess 1500 px. Then i use a canvas zoom programmed like this (another canvas where i zoom on the mouse position):
var x1, wdth1, psx1, wdth2, y1, heg1, psy1, heg2;
function drawZoom(x, y) {
$('#zoom').css({
'left': x - (parseInt($('#zoom').css('width')) / 2),
'top': y - (parseInt($('#zoom').css('height')) / 2)
});
// console.log([x,y]);
$('#zoom').show(function () {
if (x < 25) {
x1 = 0;
wdth1 = (x + zoom_level / 2) * ratio;
psx1 = (25 - x) * 2;
wdth2 = 100 - psx1;
} else if (x > width_canvas - zoom_level/ 2) {
x1 = (x - zoom_level/ 2) * ratio;
wdth1 = (width_canvas - x + zoom_level/ 2) * ratio;
psx1 = 0;
wdth2 = 100 - 2 * (x + zoom_level/ 2 - width_canvas);
} else {
x1 = (x - zoom_level/ 2) * ratio;
wdth1 = zoom_level* ratio;
psx1 = 0;
wdth2 = 100;
}
if (y < 25) {
y1 = 0;
heg1 = (y + zoom_level/ 2) * ratio;
psy1 = (25 - y) * 2;
heg2 = 100 - psy1;
} else if (y >= height_canvas - zoom_level/ 2) {
y1 = (y - zoom_level/ 2) * ratio;
heg1 = (height_canvas - y + zoom_level/ 2) * ratio - 1;
psy1 = 0;
heg2 = 100 - 2 * (y + zoom_level/ 2 - height_canvas);
} else {
y1 = (y - zoom_level/ 2) * ratio;
heg1 = zoom_level* ratio;
psy1 = 0;
heg2 = 100;
}
// console.log(heg1);
zoomctx.fillStyle = "black";
zoomctx.fillRect(0, 0, 100, 100);
zoomctx.drawImage(canvas, x1, y1, wdth1, heg1, psx1, psy1, wdth2, heg2);
});
zoomctx.beginPath();
zoomctx.moveTo(0, 50);
zoomctx.lineTo(100, 50);
zoomctx.moveTo(50, 0);
zoomctx.lineTo(50, 100);
zoomctx.closePath();
zoomctx.stroke();
}
then i save the positions of the click and use them in order to calculate a distance etc.... (another story)
so i have a reset button, when you start clicking and fail one of the 4 click needed you can restart, it deletes all the points and restarts the fonction.
ISSUES :
-Chrome: works like a charm, no problem, nice zoom, smooth navigation, restart works perfectly etc....
-Firefox: i have a black circle on the left top corner (it's the zoom but it's all black, not working), it doesn't move, i can click and restart but i can't see where i click and the process after clicking doesn't work.
-Safari: the image appears well, the zoom is ok when i first load the image, i can click 4 times and the rest of the program works fine but if i restart then the zoom is like shifted when i put my mouse somewhere and the zoom doesn't show me the right place.
so i have no issue on chrome, and 2 different issues on safari and mozilla firefox, no error in the console.
i'm a beginner so i dont know if the is some constraints as the "webkit" in CSS for JS.
Tell me if you need more info ! Thanks !
I found what was going on with firefox, it was just an update problem. my friends tested it out with their version of firefox and all was ok si i updated my firefox (thought it was doing this alone but apparently not) and it worked fine. I'm gonna try with safari if this is the same problem or not
Canvas drawing isn't supported in Firefox, but there are Mozilla specific alternatives. See https://caniuse.com/#search=Canvas
As far as your issues with the positioning after resetting the zoom in safari, please post your reset button functionality. Chances are, you are missing some reinitialization of your position/coordinate tracking.
I am trying to make a game board with p5.js. I want the circular shape of the board to have 60 tiles in total, which means the rotation should be 6 degrees (6*60 = 360 degrees), if I want 60 tiles in a circular shape.
I cannot get this to work, even though I think I'm doing it right. This is essentially what I want to do:
If I try with rotate(6) and no translate (because that apparently completely doesn't work), I get this result:
Check out my code at https://jsfiddle.net/mortenmoulder/ze6fn3av/ (you might want to resize the window and hit run again) and here:
angleMode(DEGREES);
for (let i = 0; i <= 60; i++) {
//translate(width / 2, height / 2);
rotate(6);
rect(centerW + 500, centerH, 100, 50);
}
What am I doing wrong?
Preface: I have never used p5.js before.
anyway, i tweaked your code and got pretty close:
var tileInnerWidth = 52;
var tileOuterWidth = 57
var tileHeight = 50;
translate(width / 2, height / 2);
for (let i = 0; i <= 60; i++) {
quad(-tileOuterWidth/2, centerH, tileOuterWidth/2, centerH, tileInnerWidth/2, centerH - tileHeight,-tileInnerWidth/2,centerH - tileHeight);
rotate(6);
}
Here's a fiddle: https://jsfiddle.net/mht3o21p/2/
To explain what i'm doing:
There's a single translate to move the origin to the middle of the screen, so that rotations can be relative to that
I draw the trapezoid which goes at the very bottom of the circle. It needs to be centered horizontally, so its x coordinates have division by 2. The sizes were picked by trial and error and what looked good.
I rotate 6 degrees and repeat.
JSFiddle link - http://jsfiddle.net/lustre/p0z9wp51/1/
I didn't create this code, but the blog which it was created from isn't working at the moment...
Is it possible to have the spinner center itself on the segment it lands on? For example, sometimes it may just land on segment "A", could it then shift to center itself on segment "A"? Or would that require a complete rebuild of the spinner itself?
It's worth noting that the number of segments is dynamic, in that users can add their own names.
The code below begins the spin, and calculates the angle, and total time to spin.
spin: function() {
spinAngleStart = Math.random() * 10 + 10;
spinTime = 0;
spinTimeTotal = Math.random() * 3 + 4 * 1000;
rotateWheel();
},
And this code actually rotates the wheel (afaik).
var rotateWheel = function rotateWheel() {
spinTime += 30;
if(spinTime >= spinTimeTotal) {
stopRotateWheel();
return;
}
var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal);
startAngle += (spinAngle * Math.PI / 180);
drawWheel();
spinTimeout = setTimeout(rotateWheel, 30);
}
index returns the segment/array index of the section it's landed on.
outterRadius is the radius of the outer circle. Responsively, the circle is resized with CSS percentages, would this affect the outer radius in HTML5? Or not as it's rendered before the CSS takes effect?
If it would require a complete rebuild, that's fine. Just looking for feedback on whether it's even possible with this code from people more experience in HTML5 than me :)
I'm trying to create a wheel of fortune type animation using jquery but for some reason the code that i am using always displays the wrong number!
here is the jsfiddle: http://jsfiddle.net/wf49mqaa/2/
click on the WHITE AREA in the wheel to see the animation and you will see a wrong number will be shown!
at the moment I only have 4 columns and 4 segments in my jquery code but in the future i am will pull the amount of segments from a database and I need this to work correctly at all times and display the correct number.
I tried everything from changing the segment = Math.ceil(((percent/100) * 4)), to segment = Math.ceil(((percent/100) * 4) -1), and also segment = Math.ceil(((percent/100) * 5)),
and it still display wrong number!
could someone please advise on this?
Thanks
Part of the Code you use I found in a Non-working demo from sitepoint., digging a bit deeper there are two different errors/ problems to solve the fortune-wheel behavior:
First: How to define the degree:
// existing code fragment (wrong)
var deg = 1500 + Math.round(Math.random() * 1500);
This would cause the wheel to stop at a totally random position, but that is not what you need. The wheel should always stop at the marker position, it should just turn around by a random number of segments.
// supposing you have a wheel with 4 segments (here the items array):
var deg = 0, /* basic degree */
spinbase = 1080, /* basic spinning of the wheel, here 3 times full turn */
items = [1,2,3,4];
// your spinning function...
spin: function () {
var min = 1,
max = 10,
rand = Math.floor(min + Math.random()*(max+1-min));
[...]
// like this you'll stop at the same position,
// but the wheel moved by a random number of segments
deg = deg + ( Math.round( 360 / items.length ) * rand) + spinbase;
[...]
}
Second: How to get the correct segment:
In short:
// where deg is the current degree, and items the array from above.
var segmentIndex = Math.ceil(
(deg - (360 * parseInt(deg / 360))) /
Math.round(360/items.length)
);
And when filling the algorithm..
// e.g. deg is (degree of each segment) * random (here 5) + 1080
// deg = 1530 (1080 + ( (360/4) * 5) )
// (1530 - (360 * parseInt( 1530 / 360))) / Math.round(360 / 4);
// (1530 - (360 * 4)) / 90;
// 90 / 90 = 1
// since we have 4 segments only and the random number is higher,
// the wheel did another full turn + 1 (from the starting number)
// so we get items[ 1 ] = (result: 2);
// due to the ceil/floor/round methods in calculation it can happen
// that you reach the extrem values segments.length or less than 0,
// to fix this:
var segmentIndex = Math.ceil(
(deg - (360 * parseInt(deg / 360))) /
Math.round(360/items.length)
);
if(target < 0 ) { target = segment.length - 1; }
if(target === segments.length ) { target = 0; }
alert( 'Winning: ' + items[target] );
Putting this together you'll get a working fortune-wheel. I allowed myself to create a new variant of the fortune wheel, which is able to handle different amounts of segments to make it easier to prove the algorithm.