positioning object inside container with offSet - javascript

Hey guys I’m trying to make simple point & click game, in background will be some nice landscape which will be moved by arrows on the left and right (same like slider works), I want object to move only within the lower div (#boxMap), I tried to build contructor to main object but together with friend who helps me, we wrote it in way below. The problem is that this object still doesn’t move how it should be, idea is that when game starts, and when I click right arrow, object appears on the left, if I click left arrow object appears on the left side of the div. When I click the mouse, object should moves to position I clicked. I’m kinda desperate as I have ideas how to make It work later but I totally cannot manage this positioning and movement of the object.
I found some nice yt tutorial about moving objects and tried to set statements to move only within the div without getting outside the edge, unfortunately now it moves only on the bottom edge of div.
So I created new object with class .hero inside the boxMap, and set it’s starting position with css properties, then with function getClickPosition I try to manage it’s movement, I try also set that if object is clicked outside the frame of div, it set it’s position on particular value. Unfortunately now it moves only through the bottom edge, right side it doesn’t exceed the edge, left side it does.
Hope I was able to more or less explain what I try to achieve and what I have already done.
Any idea how to set position and some simple movement in much simpler way?
I would be grateful for any tips
HTML
<body>
<div id="emptySpace">
<span class="left"></span>
<span class="right"></span>
</div>
<div id="boxMap">
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="js/app.js"></script>
</body>
CSS
body {
margin: 0;
padding: 0;
background-color: antiquewhite;
#emptySpace {
width: 100%;
height: 70vh;
background-color: azure;
position: relative;
.left {
position: absolute;
left: 10px;
top: 40vh;
border-top: 40px solid transparent;
border-bottom: 40px solid transparent;
border-right:40px solid blue;
//display: none;
}
.right {
position: absolute;
right: 10px;
top: 40vh;
border-top: 40px solid transparent;
border-bottom: 40px solid transparent;
border-left: 40px solid green;
//display: none;
}
}
#boxMap {
width: 100%;
height: 30vh;
border: 1px solid black;
background-color: #d8fffa;
position: relative;
.hero {
position: absolute;
width: 50px;
height: 50px;
display: inline-block;
border: 1px solid black;
border-radius: 50%;
background-color: blue;
transform: translate3d(0px, -45px, 0);
transition: 2s linear;
}
}
}
Javascript
function Game() {
this.hero = new Hero();
this.counter = 0;
var self = this;
this.boxMap = document.querySelector("#boxMap");
// Placing hero on the map
this.showHero = function () {
var heroElement = document.createElement('div');
heroElement.classList.add('hero');
console.log(self, self.boxMap);
self.boxMap.appendChild(heroElement);
$(heroElement).css({top: 130, left: 30})
}
}
var game = new Game();
game.showHero();
var heroMovement = document.querySelector(".hero");
var boxMap = document.querySelector("#boxMap");
boxMap.addEventListener("click", getClickPosition, false);
// Set position on hero and set movement
function getClickPosition(e) {
var xPosition = e.clientX;
var maxWidth = 1350;
var minWidth = -20;
if (xPosition < minWidth) {
xPosition = minWidth;
} else if (xPosition > maxWidth) {
xPosition = maxWidth
} else {
var xPosition = e.clientX - (heroMovement.offsetWidth + 3);
}
var yPosition = e.clientY;
var maxHeight = 60;
var minHeight = -130;
if (yPosition < minHeight) {
yPosition = minHeight;
} else if (yPosition > maxHeight) {
yPosition = maxHeight
} else {
var yPosition = e.clientY - (heroMovement.offsetHeight + 558);
}
console.log(xPosition, yPosition);
var translate3dValue = "translate3d(" + xPosition + "px" + "," + yPosition + "px, 0)";
console.log(translate3dValue);
heroMovement.style.transform = translate3dValue;
}

Related

style.left isn't working, but, style.background is working

i'm trying to do a simple task of moving a div's position when the mouse is clicked. While style.left isn't working, style.backgroundColor is working. Google hasn't been helpful, please help
`body {
background-color: aquamarine;
}
.box {
height: 120px;
width: 120px;
border-radius: 60px;
background-color: black;
}`
const moving = document.querySelector(".box");
function move() {
let x = 100;
moving.style.left = 10 + "px";
console.log("hi");
moving.style.backgroundColor = "white";
}
moving.addEventListener("click", move);
by default div have a static position
static
The element is positioned according to the normal flow of the
document.
The top, right, bottom, left, and z-index properties have no
effect. This is the default value.
https://developer.mozilla.org/fr/docs/Web/CSS/position
an idea can be to set the position of your box in relative or absolute
const moving = document.querySelector(".box");
function move() {
let x = 100;
moving.style.left = 10 + "px";
console.log("hi");
moving.style.backgroundColor = "white";
}
moving.addEventListener("click", move);
body {
background-color: aquamarine;
}
.box {
height: 120px;
position: relative;
width: 120px;
border-radius: 60px;
background-color: black;
}
`
<div class="box"></div>

Resize rotated object preserving left top corner (sub-pixel rendering issue)

I'm trying to resize rotated (transformed with css) object with vanilla js. Object origin is center and can't be changed.
I've found a function here which should do the thing, however still it doesn't looks ideal - left top corner change its position (half of pixels or so).
Here is a simplified codepen example.
What code modifications I need to do at getCorrection to keep left top corner position always same?
UPDATE:
As per comments below, the calculation is accurate but browser just can't deal with fractions of a pixel perfectly which seems like a technical limitation? Any ideas how to fix?
If you apply the transforms relative to the top left corner (transform-origin: top left), then you don't need to calculate that correction factor. You can then wrap all the shapes if you need to position them on the page:
const obj = document.querySelector('.object');
const btn = document.querySelector('button');
let h = 100;
let w = 100;
btn.addEventListener('click', () => {
h += 5;
w += 5;
updateElement();
});
function updateElement() {
obj.style.width = w + 'px';
obj.style.height = h + 'px';
}
updateElement();
body {
margin: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
overflow-y: scroll;
overflow-x: hidden;
}
button {
margin: 8px;
padding: 8px;
font-family: monospace;
border: 2px solid black;
background: transparent;
}
.container {
position: relative;
width: 100%;
height: 100%;
}
.object-initial {
position: absolute;
top: 16px;
left: 30%;
outline: 1px dashed black;
transform: rotate(20deg);
transform-origin: top left;
width: 100px;
height: 100px;
}
.object {
position: absolute;
top: 16px;
left: 30%;
outline: 1px solid black;
transform: rotate(20deg);
transform-origin: top left;
}
<button>Increase width and height</button>
<div class="container">
<div class="object"></div>
<div class="object-initial"></div>
</div>
Finally I was able to resolve issue with sub-pixels rendering. I've changed an approach slightly - instead of updating left & top coordinates with offset, I've used css translate() to apply the same offset. Works very well.
Seems transform has much better sub-pixels rendering optimizations.
Here is a codepen example.
function updateElement() {
obj.style.left = l + 'px';
obj.style.top = t + 'px';
obj.style.width = w + 'px';
obj.style.height = h + 'px';
obj.style.transform = 'rotate(' + r + 'deg) translate(' + ox + 'px, ' + oy + 'px)';
}
Same formula might be reused with slight adjustments to preserve any of rectangle corners.

vertical scrollbar moving bit by bit depending on how far you scroll

Working on a scroll bar that will be vertical and as we scroll down the brown bit will not fill up but move bit by bit depending on how far we scroll down. So esentially the brown bit will move three times down if we scroll to the bottom. So far I made a scroll bar that fills up but ideally I would like it to have the movable brown bit like in the example in the attached picture. Anyone able to help out?
My code so far looks like this:
window.onscroll = () => {
var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
var height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
var scrolled = (winScroll / height) * 100;
document.getElementsByClassName("scroll-bar__inner")[0].style.height = scrolled + "%";
};
.scroll-bar {
position: fixed;
top: 50%;
right: 34px;
width: 2.5px;
height: 80px;
background-color: #959595;
display: block;
transform: translateY(-50%);
}
.scroll-bar__inner:first-of-type {
height: 20%;
background: #ffffff;
}
.scroll-bar__inner:nth-of-type(2) {
/* height: 20%; */
background: #ffffff;
}
#mock-content {
width: 150px;
height: 500px;
border: 3px solid red;
border-radius: 5px;
}
<div class="scroll-bar">
<div class="scroll-bar__inner"></div>
</div>
<div id="mock-content">
This div represents some content that causes the body to scroll.
</div>
It was a bit confusing what you were trying to do with your original CSS. I couldn't see why you would alter the height of the container for the scroll bar, instead of just repositioning the block within a full heigh container (i.e. .scroll-bar__inner). In any case here is a snippet that I think accomplishes what you're trying to do:
window.onscroll = () => {
var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
var height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
var containerHeight = document.getElementsByClassName("scroll-bar")[0].clientHeight;
// range from 0 to x% where x% is 100% - (80 / scroll bar height * 100)
// This makes it so the bar doesn't extend off the page.
var scrolled = (winScroll / height) * ((containerHeight - 80) / containerHeight) * 100;
document.getElementsByClassName("scroll-bar__inner")[0].style.top = scrolled + '%';
};
.scroll-bar {
position: fixed;
top: 0;
bottom: 0;
right: 34px;
width: 5px;
background-color: whitesmoke;
}
.scroll-bar__inner {
height: 80px;
background: #333;
position: relative;
}
#mock-content {
width: 150px;
height: 500px;
border: 3px solid red;
border-radius: 5px;
}
<div class="scroll-bar">
<div class="scroll-bar__inner"></div>
</div>
<div id="mock-content">
This div represents some content that causes the body to scroll.
</div>

JavaScript Moving transition without the use of CSS

I'm using a small script to follow the cursor with a div element.
This script makes the element strictly follow the cursor.
What I'm trying to do is to add some kind of duration to the process of "following" the cursor. I tried CSS transitions but the animation always ended up breaking. Can somebody please help me with this?
Let's say mouse is somewhere, and then it changes position by around 100px. I want to specify the duration like if i was using CSS... But the thing is that I can not use any transitions but only some javascript magic instead...
document.body.addEventListener("mousemove", function(e) {
var curX = e.clientX;
var curY = e.clientY;
document.querySelector('mouse').style.left = curX - 10 + 'px';
document.querySelector('mouse').style.top = curY - 10 + 'px';
});
body {
background: #333;
height: 500px;
width: 500px;
}
mouse {
display: block;
position: fixed;
height: 20px;
width: 20px;
background: #fff;
border-radius: 50%;
}
<body>
<mouse></mouse>
</body>
I was wondering how to add a transition without using the CSS but I'm not the most advanced when it comes to JavaScript.
[edit] : I don't wanna use window.setTimeout.
[edit 2] : I wanted to use transition: 0.1s; but as I said it broke the effect when user moved the mouse too quickly.
There's a whole bunch of ways to do this, as you can see in the other answers, each with its own "feel". I'm just adding one more, where the dot approaches the cursor by a percentage of the remaining distance.
let curX = 0, curY = 0, elemX = null, elemY = null;
document.body.addEventListener("mousemove", function(e) {
curX = e.clientX;
curY = e.clientY;
if (elemX === null) [ elemX, elemY ] = [ curX, curY ];
});
let amt = 0.1; // higher amount = faster tracking = quicker transition
let elem = document.querySelector('mouse');
let frame = () => {
requestAnimationFrame(frame);
elemX = (elemX * (1 - amt)) + (curX * amt);
elemY = (elemY * (1 - amt)) + (curY * amt);
elem.style.left = `${elemX}px`;
elem.style.top = `${elemY}px`;
};
frame();
body {
position: absolute;
background: #333;
left: 0; top: 0; margin: 0; padding: 0;
height: 100%;
width: 100%;
}
mouse {
display: block;
position: absolute;
height: 20px; margin-left: -10px;
width: 20px; margin-top: -10px;
background: #fff;
border-radius: 50%;
}
<body>
<mouse></mouse>
</body>
You can use setTimeout() function, to introduce a delay:
document.body.addEventListener("mousemove", function(e) {
var delay=250 //Setting the delay to quarter of a second
setTimeout(()=>{
var curX = e.clientX;
var curY = e.clientY;
document.querySelector('mouse').style.left = curX - 10 + 'px';
document.querySelector('mouse').style.top = curY - 10 + 'px';
},delay)
});
body {
background: #333;
height: 500px;
width: 500px;
}
mouse {
display: block;
position: fixed;
height: 20px;
width: 20px;
background: #fff;
border-radius: 50%;
}
<body>
<mouse></mouse>
</body>
Or, to avoid trailing, use an interval and move the cursor to the correct direction (change ratio to set the speed ratio):
var curX,curY
document.body.addEventListener("mousemove", function(e) {
curX = e.clientX;
curY = e.clientY;
});
setInterval(()=>{
var ratio=5
var x=document.querySelector('mouse').offsetLeft+10
var y=document.querySelector('mouse').offsetTop+10
document.querySelector('mouse').style.left=((curX-x)/ratio)+x-10+"px"
document.querySelector('mouse').style.top=((curY-y)/ratio)+y-10+"px"
},16)
body {
background: #333;
height: 500px;
width: 500px;
}
mouse {
display: block;
position: fixed;
height: 20px;
width: 20px;
background: #fff;
border-radius: 50%;
}
<body>
<mouse></mouse>
</body>

Dynamically recalculate mouse position for a selection box when changing page size

I have a script to draw a selection box over a grid (just a .png image) however I have an error where the selection box is drawn in the wrong place.
I think it's because the script which the mousedown position uses calculates top and left on page load. If the page is resized before creating a selection box, it uses the original calculations of top and left and is therefore not in the correct position.
Is there a way to fix this problem without completely bastardising my script?
Below is the code used along with a .zip and a jsFiddle, thank you for your help!
jsFiddle
.zip
CSS:
body{
background-color: #3AB3F0;
}
#board-background{
width: 1000px;
height: 1000px;
padding: 25px 25px 25px 25px;
margin: 25px auto 25px;
position: relative;
background: url(https://abs.twimg.com/a/1366134123/t1/img/wash-white-30.png);
border: 0px solid #e5e5e5;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05);
box-shadow: 0 1px 2px rgba(0,0,0,.05);
}
#board {
position: absolute;
background-color: #FFF;
z-index: 1;
width: 1000px;
height: 1000px;
border-bottom: 1px solid black;
border-right: 1px solid black;
}
#board img {
position: absolute;
z-index: 2;
user-drag: none;
-moz-user-select: none;
-webkit-user-drag: none;
}
#selectionBox {
position: absolute;
z-index: 3;
display: none;
background-color: red;
min-width: 0px;
min-height: 0px;
width: 10px;
height: 10px;
opacity: 0.8;
}
HTML:
<html>
<head>
<link href="css/test.css" rel="stylesheet">
<script type="text/javascript" src="http://code.jquery.com/jquery-git.js"></script>
<script type="text/javascript" src="js/board_script.js"></script>
</head>
<body>
</body>
</html>
JS:
// GRID CREATION SCRIPT //
// -------------------- //
function creategrid(){
//Outside background for the board
var BoardBackground = document.createElement('div');
BoardBackground.id = 'board-background';
BoardBackground.class = 'board-background';
document.body.appendChild(BoardBackground);
//Generated image
var Board = document.createElement("div");
Board.id = 'board';
Board.className = 'board';
BoardBackground.appendChild(Board);
//grid image
var grid = document.createElement("img");
grid.id = 'grid';
grid.className = 'grid';
grid.src = "media/grid.png";
Board.appendChild(grid);
}
// Selection Box Script //
// -------------------- //
var isDragging = false,
dragStart,
cellSpacing = 10,
gridOffset,
selectionBox;
function getMousePos (e) {
return {
'left': Math.floor((e.pageX - gridOffset.left) / cellSpacing) * cellSpacing .toFixed( 0 ),
'top': Math.floor((e.pageY - gridOffset.top) / cellSpacing) * cellSpacing .toFixed( 0 )
};
};
$(document).ready(function(){
creategrid(10);
gridOffset = $('#board').offset();
selectionBox = $('<div>').attr({id: 'selectionBox'})
.appendTo($('#board'));
$('#board').on('mousedown', function(e){
isDragging = true;
var pos = getMousePos(e);
dragStart = pos;
selectionBox.css({
left: pos.left,
top: pos.top,
width: 10,
height: 10
}).show();
});
$('#board').on('mousemove', function(e){
if(!isDragging)
return false;
var pos = getMousePos(e);
var diff = {
'left': pos.left - dragStart.left,
'top': pos.top - dragStart.top
};
selectionBox.css({
left: Math.min(pos.left, dragStart.left),
top: Math.min(pos.top, dragStart.top),
width: Math.abs(diff.left),
height: Math.abs(diff.top)
});
});
$('#board').on('mouseup', function(e){
isDragging = false;
});
});
Media:
(I need 10 rep to post a third link, so here's plaintext and 'code')
oi43.tinypic.com/33opjtd.jpg
[grid.png](http://oi43.tinypic.com/33opjtd.jpg "grid lined image with transparent background")
Other things that I need help with:
another minor error is the fact that, when selecting to the left and the top, the box rotates around the top left corner rather than the bottom right (try selecting the entire grid from the bottom right square).
I think that this has something to do with putting an if statement around the math.abs in the css and subtracting 10px from either side... but I can't work it out
Also in the future I want to be able for the user to upload an image and have it displayed over the selection box (dynamically changing in size) it should be possible by changing the css of the selection box... I might open a separate question for that though.
A single line can solve your selection probleme on resize :
$(window).resize(function(){gridOffset = $('#board').offset();})

Categories