I just adapted this code to play a sequence of images when you move the mouse on the x axis. I have two questions about this code:
I can't adapt the length to the width of the div, not to the width of the whole window.
There are "flashes" when I move the mouse, I have the impression that it comes from the loading of the images?
Thanks for your help.
Live: https://codepen.io/nicolastilly/pen/jOBKxKK
Code:
var blocWidth;
var imgblock = ['https://assets.codepen.io/265602/frame0.png',
'https://assets.codepen.io/265602/frame1.png',
'https://assets.codepen.io/265602/frame2.png',
'https://assets.codepen.io/265602/frame3.png',
'https://assets.codepen.io/265602/frame4.png',
'https://assets.codepen.io/265602/frame5.png',
'https://assets.codepen.io/265602/frame6.png',
'https://assets.codepen.io/265602/frame7.png',
'https://assets.codepen.io/265602/frame8.png',
'https://assets.codepen.io/265602/frame9.png',
'https://assets.codepen.io/265602/frame10.png',
'https://assets.codepen.io/265602/frame11.png',
'https://assets.codepen.io/265602/frame12.png',
'https://assets.codepen.io/265602/frame13.png',
'https://assets.codepen.io/265602/frame14.png',
'https://assets.codepen.io/265602/frame15.png',
'https://assets.codepen.io/265602/frame16.png',
'https://assets.codepen.io/265602/frame17.png',
'https://assets.codepen.io/265602/frame18.png',
'https://assets.codepen.io/265602/frame19.png',
'https://assets.codepen.io/265602/frame20.png'];
function onMouseMove(e) {
var x = e.pageX;
var theimg = imgblock[parseInt(x / blocWidth * imgblock.length)];
$('.bloc1').css("background-image", "url('" + theimg + "')");
}
function onResize() {
blocWidth = $('.bloc1').width();
}
function onResize() {
blocWidth = $(document).width();
}
$(window).on('mousemove', onMouseMove);
$(window).resize(onResize);
onResize();
As for flickering, you can circumvent that by preloading the images
let loaded = 0 ;
imgblock.forEach(url => {
let img=new Image();
img.src=url;
img.onload = onImgLoaded
})
function onImgLoaded() {
loaded++;
if (loaded == imgblock.length-1) console.log('all images have pre-loaded');
}
As for the sizing issue, you need to take into account if you're mousing over the target and accounting for the div's left value in your calculations. Get rid of both these:
function onResize() {
blocWidth = $('.bloc1').width();
}
function onResize() {
blocWidth = $(document).width();
}
replace with:
let blocStartX;
function onResize() {
let pos = $(".bloc1")[0].getBoundingClientRect()
console.log(pos)
blocWidth = pos.width;
blocStartX = pos.x
}
Detect the mouseover:
let isOnDiv = false
$(".bloc1").mouseenter(function(){isOnDiv=true;});
$(".bloc1").mouseleave(function(){isOnDiv=false;});
and add this to the top of your mousemove function:
if (!isOnDiv) return
var x = e.pageX - blocStartX;
https://codepen.io/john-tyner/pen/wvJXXQZ
Related
I am using pdf.js to view the pdf, and adding canvas over the top of the each page to add image on click. Images are adding on mouse click but its not on exact position..
$('.page').on('click', function(e) {
console.log("fgdfgdfg")
console.log(e)
if($("#hotspotTool").hasClass('active')){
var pageIndex = window.PDFViewerApplication.pdfViewer.currentPageNumber;
var page = window.PDFViewerApplication.pdfViewer.getPageView(pageIndex);
if ($(e.target).parent().hasClass('page')) {
var pid = $(e.target).parent().attr("id");
console.log(pid)
var canvas=document.getElementById(pid).getElementsByClassName("annotationLayer_svg")[0]
console.log(canvas)
var canv_id = $(canvas).attr('id')
if(canvas){
count++
var ctx = canvas.getContext("2d");
base_image = new Image();
base_image.src = '/images/dot.png';
base_image.id="hotspot"+count
base_image.draggable="true"
console.log(base_image)
var x = e.offsetX - base_image.width/2,
y = e.offsetY - base_image.height/2;
console.log(x,y)
ctx.drawImage(base_image,x,y);
ctx.save();
$("#hotspotTool").removeClass("active");
$('#hotspotmodel').modal('show');
$('#submit').on('click', function() {
$(this).data('clicked', true);
$("#hotspotform").valid();
if( $("#hotspotform").valid()){
$('#hotspotmodel').modal('hide');
var page_value = document.getElementById("pagelink").value
var spot_link = window.location.href+"page="+page_value
var anchor = document.createElement("A");
anchor.appendChild(base_image)
anchor.setAttribute("href", spot_link);
}
});
}
}
}
})
How can I able to add image on mouse click on exact position
I have list of mouse cords (x,y)
Because it is impossible to move mouse with javascript, I would like to print a fake mouse on the document followed by the cords that I already have.
Is it possible? How?
My solution was to print PNG that shows the mouse and hide the original one with css:
createCursor: function() {
var cursor = document.createElement("img");
cursor.src = chrome.extension.getURL('pics/cursor.png');
cursor.style.zIndex = "9999";
cursor.setAttribute("id", "recordMeCursor");
var body = document.getElementsByTagName("BODY")[0];
body.style.cursor = 'none';
body.appendChild(cursor);
},
moveCursor: function(cords, i, callback) {
var cursor = document.getElementById("recordMeCursor");
setTimeout(function() {
cursor.style.position = "absolute";
cursor.style.left = cords.x+'px';
cursor.style.top =cords.y+'px';
return callback('OK');
}, i * 50);
},
destroyMouse: function() {
var cursor = document.getElementById("recordMeCursor");
cursor.parentNode.removeChild(cursor);
var body = document.getElementsByTagName("BODY")[0];
body.style.cursor = 'default';
}
I'm having trouble creating a click event for my Javascript canvas game. So far I have been following a tutorial, however the way you interact with the game is through mouse hover. I would like to change it so that instead of hovering over objects in the canvas to interact, I instead use a mouse click.
The following is the code I use to detect the mouse hover.
getDistanceBetweenEntity = function (entity1,entity2) //return distance
{
var vx = entity1.x - entity2.x;
var vy = entity1.y - entity2.y;
return Math.sqrt(vx*vx+vy*vy);
}
testCollisionEntity = function (entity1,entity2) //return if colliding
{
var distance = getDistanceBetweenEntity(entity1,entity2);
return distance < 50;
}
I then use this in a loop to interact with it.
var isColliding = testCollisionEntity(player,nounList[key]);
if(isColliding)
{
delete nounList[key];
player.score = player.score + 10;
}
Below is a complete copy of my game at its current state.
<canvas id="ctx" width="500" height="500" style="border:1px solid #000000;"></canvas>
<script>
var ctx = document.getElementById("ctx").getContext("2d");
ctx.font = '30px Arial';
//Setting the height of my canvas
var HEIGHT = 500;
var WIDTH = 500;
//Player class
var player =
{
x:50,
spdX:30,
y:40,
spdY:5,
name:'P',
score:0,
};
//Creating arrays
var nounList ={};
var adjectivesList ={};
var verbsList ={};
getDistanceBetweenEntity = function (entity1,entity2) //return distance
{
var vx = entity1.x - entity2.x;
var vy = entity1.y - entity2.y;
return Math.sqrt(vx*vx+vy*vy);
}
testCollisionEntity = function (entity1,entity2) //return if colliding
{
var distance = getDistanceBetweenEntity(entity1,entity2);
return distance < 50;
}
Nouns = function (id,x,y,name)
{
var noun =
{
x:x,
y:y,
name:name,
id:id,
};
nounList[id] = noun;
}
Adjectives = function (id,x,y,name)
{
var adjective =
{
x:x,
y:y,
name:name,
id:id,
};
adjectivesList[id] = adjective;
}
Verbs = function (id,x,y,name)
{
var verb =
{
x:x,
y:y,
name:name,
id:id,
};
verbsList[id] = verb;
}
document.onmousemove = function(mouse)
{
var mouseX = mouse.clientX;
var mouseY = mouse.clientY;
player.x = mouseX;
player.y = mouseY;
}
updateEntity = function (something)
{
updateEntityPosition(something);
drawEntity(something);
}
updateEntityPosition = function(something)
{
}
drawEntity = function(something)
{
ctx.fillText(something.name,something.x,something.y);
}
update = function ()
{
ctx.clearRect(0,0,WIDTH,HEIGHT);
drawEntity(player);
ctx.fillText("Score: " + player.score,0,30);
for(var key in nounList)
{
updateEntity(nounList[key]);
var isColliding = testCollisionEntity(player,nounList[key]);
if(isColliding)
{
delete nounList[key];
player.score = player.score + 10;
}
}
for(var key in adjectivesList)
{
updateEntity(adjectivesList[key])
var isColliding = testCollisionEntity(player,adjectivesList[key]);
if(isColliding)
{
delete adjectivesList[key];
player.score = player.score - 1;
}
}
for(var key in verbsList)
{
updateEntity(verbsList[key])
var isColliding = testCollisionEntity(player,verbsList[key]);
if(isColliding)
{
delete verbsList[key];
player.score = player.score - 1;
}
}
if(player.score >= 46)
{
ctx.clearRect(0,0,WIDTH,HEIGHT);
ctx.fillText("Congratulations! You win!",50,250);
ctx.fillText("Refresh the page to play again.",50,300);
}
}
Nouns('N1',150,350,'Tea');
Nouns('N2',400,450,'Park');
Nouns('N3',250,150,'Knee');
Nouns('N4',50,450,'Wall');
Nouns('N5',410,50,'Hand');
Adjectives('A1',50,100,'Broken');
Adjectives('A2',410,300,'Noisy');
Verbs('V1',50,250,'Smell');
Verbs('V2',410,200,'Walk');
setInterval(update,40);
To summarize all I want to do is change it so that instead of mousing over words to delete them you have to click.
(Apologies for not using correct terminology in places, my programming knowledge is quite limited.)
You can have your canvas listen for mouse clicks on itself like this:
// get a reference to the canvas element
var canvas=document.getElementById('ctx');
// tell canvas to listen for clicks and call "handleMouseClick"
canvas.onclick=handleMouseClick;
In the click handler, you'll need to know the position of your canvas relative to the viewport. That's because the browser always reports mouse coordinates relative to the viewport. You can get the canvas position relative to the viewport like this:
// get the bounding box of the canvas
var BB=canvas.getBoundingClientRect();
// get the left X position of the canvas relative to the viewport
var BBoffsetX=BB.left;
// get the top Y position of the canvas relative to the viewport
var BBoffsetY=BB.top;
So your mouseClickHandler might look like this:
// this function will be called when the user clicks
// the mouse in the canvas
function handleMouseClick(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// get the canvas postion relative to the viewport
var BB=canvas.getBoundingClientRect();
var BBoffsetX=BB.left;
var BBoffsetY=BB.top;
// calculate the mouse position
var mouseX=e.clientX-BBoffsetX;
var mouseY=e.clientY-BBoffsetY;
// report the mouse position using the h4
$position.innerHTML='Click at '+parseInt(mouseX)+' / '+parseInt(mouseY);
}
If your game doesn't let the window scroll or resize then the canvas postion won't change relative to the viewport. Then, for better performance, you can move the 3 lines relating to getting the canvas position relative to the viewport to the top of your app.
// If the browser window won't be scrolled or resized then
// get the canvas postion relative to the viewport
// once at the top of your app
var BB=canvas.getBoundingClientRect();
var BBoffsetX=BB.left;
var BBoffsetY=BB.top;
function handleMouseClick(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the mouse position
var mouseX=e.clientX-BBoffsetX;
var mouseY=e.clientY-BBoffsetY;
// report the mouse position using the h4
$position.innerHTML='Click at '+parseInt(mouseX)+' / '+parseInt(mouseY);
}
i am trying to drag and drop an image of a card on the canvas of the javascript but the mouseup event does not seem to be working even though it is inside the main(). Once the card is selected, it follows around the mouse but it does not seem to let go when i let go of the mouse. I also know that the image is repeated from not clearing the screen.
function main(){
var ctx = cvs.getContext("2d");
var img = new Image();
img.src = "allcards.png";
var imgX = 75;
var imgY = 75;
draw();
function draw(){
ctx.drawImage(img,0,0,97,129,imgX,imgY,100,100);
}
cvs.addEventListener("mouseup", function(ev){
greaterX = false;
lessX = false;
greaterY = false;
lessY = false;
}
);
cvs.addEventListener("mousedown", function(ev){
if(ev.clientX <= (imgX + 97)){
var greaterX = true;
}
if(ev.clientY <= (imgY + 129)){
var greaterY = true;
}
if(ev.clientX >= imgX){
var lessX = true;
}
if(ev.clientY >= imgY){
var lessY = true;
}
if(greaterX == true)
if(greaterY == true)
if(lessX == true)
if(lessY == true)
cvs.addEventListener("mousemove", function(ev){
var offsetX = (ev.clientX - imgX);
var offsetY = (ev.clientY - imgY);
imgX = imgX + offsetX;
imgY = imgY + offsetY;
draw();
});
});
};
greaterX, lessX, etc, are all defined with var inside of your mousedown function, meaning their scope is limited to the mousedown function only.
Therefore, it is useless to try and set them back to false inside of your mouseup function. You need to declare your variables in the main part of your function:
function main() {
var greaterX, lessX, greaterY, lessY;
var ctx = cvs.getContext("2D");
//etc...
Now, simply setting greaterX, lessX, etc back to false is not enough, because the mousemove event checker inside of mousedown is still active. When you apply an event listener, it stays there until you remove it.
So, the next step is to separate the mousemove event function into it's own function (I used "mouseMoveHandler" for the name) and remove the event listener using .removeEventListener(type, listener, useCapture) inside of mouseup.
The mousemove function:
function mouseMoveHandler(ev) {
offsetX = (ev.clientX - imgX);
offsetY = (ev.clientY - imgY);
imgX = imgX + offsetX;
imgY = imgY + offsetY;
draw();
}
The mousedown function (important part):
if (greaterX === true) { //you need the brackets for multi-line if statements
if (greaterY === true) {
if (lessX === true) {
if (lessY === true) {
cvs.addEventListener("mousemove",mouseMoveHandler,false);
}
}
}
}
And finally, the mouseup function:
cvs.addEventListener("mouseup", function(ev) {
greaterX = false;
lessX = false;
greaterY = false;
lessY = false;
cvs.removeEventListener('mousemove',mouseMoveHandler,false);
});
Here's a jsFiddle with the solution, but not using your image.
I've figured out the centering and resizing issues, but I still can't get onLoad to work properly. Anyone have any ideas? I thought onLoad was supposed to wait for the image to be loaded completely before firing the code. As this is right now, it resizes the img for the next img, and fades it in before it's loaded, so the previous image fades in again. Once the show has run through once, it works perfectly, so obviously it's not waiting for the image to load completely before firing imageLoad().
<div id="slideShow">
<div id="slideShowImg" style="display:none; margin-right:auto; margin-left:auto;">
</div>
<div id="slideShowThumbs">
</div>
<script type="text/javascript">
loadXMLDoc('http://www.thehoppr.com/hopspots/82/82.xml', function() {
var slideShow = document.getElementById('slideShow');
var items = [];
var nl = xmlhttp.responseXML.getElementsByTagName('image');
var i = 0;
var t;
var slideShowImg = document.getElementById('slideShowImg');
var slideShow = document.getElementById('slideShow');
var maxHeight = 300;
var maxWidth = 800;
var imgNode = new Image();
function image() {
var nli = nl.item(i);
var src = nli.getAttribute('src').toString();
var width = parseInt(nli.getAttribute('width').toString());
var height = parseInt(nli.getAttribute('height').toString());
imgNode.onLoad = imageLoad();
imgNode.src = src;
imgNode.height = height;
imgNode.width = width;
imgNode.setAttribute("style", "margin-right:auto; margin-left:auto; display:block;");
var ratio = maxHeight / maxWidth;
if (imgNode.height / imgNode.width > ratio) {
// height is the problem
if (imgNode.height > maxHeight) {
imgNode.width = Math.round(imgNode.width * (maxHeight / imgNode.height));
imgNode.height = maxHeight;
}
} else {
// width is the problem
if (imgNode.width > maxHeight) {
imgNode.height = Math.round(imgNode.height * (maxWidth / imgNode.width));
imgNode.width = maxWidth;
}
}
}
function imageLoad() {
slideShowImg.appendChild(imgNode);
Effect.Appear('slideShowImg', {
duration: 1
});
t = setTimeout(nextImage, 7000);
}
function nextImage() {
slideShowImg.setAttribute("style", "display:none");
if (i < nl.length - 1) {
i++;
image();
} else {
i = 0;
image();
}
}
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
//XML Loaded, create the slideshow
alert(xmlhttp.responseText);
image();
}
});
</script>
Here are some of my thoughts (it's all open discussion)...
Preloading - Since you're limited to downloading 2 resources in parallel per hostname, it may not make sense to preload everything up front. Since it's a slideshow, how about modifying image() to download the i + 1 image through an Image object that doesn't get appended to the DOM? It doesn't seem beneficial to prefetch i + 2 and beyond in a slideshow setting.
Centering the images - Regarding using the auto margin width for horizontal centering, I believe you'll have to explicitly set the image to display:block. Obviously that doesn't solve centering vertically. Are all the images the same size? Also, will the slideShowImg div have a defined height/width? If so, then some math can be applied to achieve the centering.
Hope this helps! :)
Ok so I fixed the issue with the onLoad, and I even added a little preloading to help the slideshow along as well. All I had to do was define the onload first, and then do everything else except define the src inside the onload's function. I then added a little nextImg preloading so there's little if no hiccup between images even if it's the first time the browser is loading them. Here's the final code for anyone who has similar onload issues and finds there way here:
<div id="slideShow">
<div id="slideShowImg" style="display:none; margin-right:auto; margin-left:auto;">
</div>
<div id="slideShowThumbs">
</div>
<script type="text/javascript">
loadXMLDoc('/82.xml', function() {
var slideShow = document.getElementById('slideShow');
var items = [];
var nl = xmlhttp.responseXML.getElementsByTagName('image');
var i = 0;
var t;
var slideShowImg = document.getElementById('slideShowImg');
var slideShow = document.getElementById('slideShow');
var maxHeight = 300;
var maxWidth = 800;
var curImg = new Image();
var nextImg = new Image();
function image() {
var cli = nl.item(i);
var src = cli.getAttribute('src').toString();
var width = parseInt(cli.getAttribute('width').toString());
var height = parseInt(cli.getAttribute('height').toString());
curImg.onload = function() {
curImg.height = height;
curImg.width = width;
curImg.setAttribute("style", "margin-right:auto; margin-left:auto; display:block;");
slideShowImg.appendChild(curImg);
var ratio = maxHeight / maxWidth;
if (curImg.height / curImg.width > ratio) {
// height is the problem
if (curImg.height > maxHeight) {
curImg.width = Math.round(curImg.width * (maxHeight / curImg.height));
curImg.height = maxHeight;
}
} else {
// width is the problem
if (curImg.width > maxHeight) {
curImg.height = Math.round(curImg.height * (maxWidth / curImg.width));
curImg.width = maxWidth;
}
}
}
curImg.src = src;
if (i < nl.length - 1) {
var nli = nl.item(i + 1);
var nsrc = nli.getAttribute('src').toString();
nextImg.src = nsrc;
}
Effect.Appear('slideShowImg', {
duration: 1
});
t = setTimeout(nextImage, 7000);
}
function imageLoad() {}
function nextImage() {
slideShowImg.removeChild(curImg);
slideShowImg.setAttribute("style", "display:none");
if (i < nl.length - 1) {
i++;
image();
} else {
i = 0;
image();
}
}
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
//XML Loaded, create the slideshow
alert(xmlhttp.responseText);
image();
}
});
</script>