CSS JQuery Rotate image inside container - javascript

I am trying to rotate an image inside a container.
The container width must have a limit, and so should the height.
I was able to accomplish it with the following code, but there is an issue with one of the images.
When image 2 is rotated sideways, or 45 degrees, it jumps outside of the borders.
It works fine for the other images.
What am I doing wrong?
var degrees = 0;
function x () {
var setRotator = (function () {
var setRotation,
setScale,
offsetAngle,
originalHeight,
originalFactor;
setRotation = function (degrees, scale, element) {
element.style.webkitTransform = 'rotate(' + degrees + 'deg) scale(' + scale + ')';
element.style.transform = 'rotate(' + degrees + 'deg) scale(' + scale + ')';
};
getScale = function (degrees) {
var radians = degrees * Math.PI / 180,
sum;
if (degrees < 90) {
sum = radians - offsetAngle;
} else if (degrees < 180) {
sum = radians + offsetAngle;
} else if (degrees < 270) {
sum = radians - offsetAngle;
} else {
sum = radians + offsetAngle;
}
return (originalHeight / Math.cos(sum)) / originalFactor;
};
return function (inner) {
offsetAngle = Math.atan(inner.offsetWidth / inner.offsetHeight);
originalHeight = inner.offsetHeight;
originalFactor = Math.sqrt(Math.pow(inner.offsetHeight, 2) + Math.pow(inner.offsetWidth, 2));
return {
rotate: function (degrees) {
setRotation (degrees, getScale(degrees), inner);
}
}
};
}());
var //outer = document.getElementById('outer'),
inner = document.getElementById('testImg'),
rotator = setRotator(testImg);
degrees += 45;
if (degrees >= 360) {
degrees = 0;
}
rotator.rotate(degrees);
}
$('#btn1').click(function(){
x()
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button onclick=document.getElementById('testImg').src='https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/smile.svg'>Img1</button>
<button onclick=document.getElementById('testImg').src='https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/juanmontoya_lingerie.svg'>Img2</button>
<button onclick=document.getElementById('testImg').src='https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/php.svg'>Img3</button>
<button type="button" id="btn1" >Rotate Div</button>
<DIV id="container" style=" width:60%;">
<DIV id="outer" width="100%" style=" position: relative;border-style:dotted;">
<img id ="testImg" src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/smile.svg" style="width:100%;height:100%;border-style:dotted;border-color:red;">
</DIV>
</DIV>

You'll need to position the image absolutely within the div and edit the scale so that it's positive and never greater than 1. I have a working version here: https://jsfiddle.net/e9h30w1z/
I've added this CSS
img {
max-width: 100%;
max-height: 100%;
height: auto;
width: auto;
position: absolute;
object-fit: contain;
left: 50%;
transform: translate(-50%);
}
#outer {
height: 0;
padding-bottom: 100%;
}
I edited the CSS rule being added within the setRotation method to include translate(-50%) (this is part of the image positioning)
And I changed the return statement of the getScale method to the following:
scale = (originalHeight / Math.cos(sum)) / originalFactor;
return (Math.abs(scale) > 1) ? 1 : Math.abs(scale);
As a note, negative scale flips the image which is why one of my attempts started skipping positions.

Related

Smooth movement with background mousemove script

I found out how to make a background image move with mouse movement.
I'm trying to figure out how to smooth the movement out or make it more fluid.
The most I've figured out is that by dividing by larger and larger numbers there is less background movement, but that's as far as I've gotten.
html
<body id="body">
</body>
css
html {
width: 100%;
}
body {
background-image: url("http://sherly.mobile9.com/download/media/656/49_ybQFKMAV.png");
background-repeat: no-repeat;
background-size: cover;
height:4400px;
}
jquery
$(document).ready(function(){
$('#body').css('background-position', 'calc(45% - 0px)');
$('#body').mousemove(function(e){
var x = -(e.pageX + this.offsetLeft) / 205;
var y = -(e.pageY + this.offsetTop) / 100;
$(this).css('background-position', "calc( 45% - " + x + 'px' + ")" + y + 'px');
});
});
A good example would be from flickr after zooming in on an image.
Or this guy's site: http://ericportfolio.com/
You should separate out the background into an element of its own.
Style the element with:
.background
{
will-change: transform;
}
To prime the rending engine to promote it to its own compositing layer. This makes transform changes more cheap.
Animate transform via translateX(...) translateY(...) instead of background position.
To smooth movement you can keep track of the last several position changes and average them.
Example with 10 sample smoothing:
const root = document.querySelector(".root");
const bg = document.querySelector(".background");
const positions = [];
root.addEventListener("mousemove", e => {
const x = -(e.pageX + bg.offsetLeft) / 50;
const y = -(e.pageY + bg.offsetTop) / 50;
positions.push({ x, y });
const averageCount = 10;
if (positions.length > averageCount)
positions.splice(0, 1);
const current = positions.reduce((acc, e) => { acc.x += e.x; acc.y += e.y; return acc }, { x: 0, y: 0 });
current.x /= positions.length;
current.y /= positions.length;
bg.style.transform = `translateX(${current.x}px) translateY(${current.y}px)`;
});
.root
{
position: relative;
}
.background
{
will-change: transform;
width: 100%;
height: 100%;
}
.overlay
{
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
}
<div class="root">
<img class="background" src="https://i.stack.imgur.com/35oj3.png"/>
<h1 class="overlay">Lorem Ipsum</h1>
</div>

javascript breathing element - Animation seems to act up after long period of time

So this is my first code snippet that I wrote for fun as part of an exercise. I created a 300 × 300 px box where two corners have their border-radius increased and decreased to create a breathing animation. After a couple of minutes, the animations seem to speed up and flutter.
Does anyone have any idea how to improve the code?
function frame() {
var elem = document.getElementById('box1');
var radius = 0;
var id1 = setInterval(frame1, 20);
var id2 = setInterval(frame2, 20);
function frame1() {
if (radius == 300) {
clearInterval(id1);
setInterval(frame2, 20);
} else {
clearInterval(id2);
radius ++;
elem.style.borderTopRightRadius = radius + 'px';
elem.style.borderBottomLeftRadius = radius + 'px';
}
return radius;
}
function frame2() {
if (radius == 0) {
clearInterval(id2);
setInterval(frame1, 20);
} else {
clearInterval(id1);
radius --;
elem.style.borderTopRightRadius = radius + 'px';
elem.style.borderBottomLeftRadius = radius + 'px';
}
return radius;
}
}
frame()
Using requestAnimationFrame and basing the animation on elapsed time, and some dirty maths
var elem = document.getElementById("box1");
var radius = 0;
var begin;
function frame(v) {
if(begin === undefined) {
begin = v;
}
let radius = Math.abs((300 + (v - begin) / 20) % 600 - 300);
elem.style.borderTopRightRadius = radius + "px";
elem.style.borderBottomLeftRadius = radius + "px";
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
#box1 {
width:500px;
height:500px;
background:dodgerblue;
}
<div id="box1">
</div>
But CSS animation is probably better for such a simple animation
#box1 {
width:500px;
height:500px;
background:dodgerblue;
}
#box1 {
animation:breath 6s infinite linear alternate;
}
#keyframes breath {
from { border-radius: 0 0 0 0; }
to { border-radius: 0 300px 0 300px; }
}
<div id="box1">
</div>
Here's the above two methods, side by side
var elem = document.getElementById("box1");
var radius = 0;
var begin;
var maxRadius = 100;
var x = 60;
function frame(v) {
if(begin === undefined) {
begin = v;
}
let radius = Math.abs((maxRadius + (v - begin) / x) % (maxRadius *2) - maxRadius);
elem.style.borderTopRightRadius = radius + "px";
elem.style.borderBottomLeftRadius = radius + "px";
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
#box1 {
width:200px;
height:200px;
background:dodgerblue;
display:inline-block;
margin-right:20px;
}
#box2 {
width:200px;
height:200px;
background:dodgerblue;
display:inline-block;
}
#box2 {
animation:breath 6s infinite linear alternate;
}
#keyframes breath {
from { border-radius: 0 0 0 0; }
to { border-radius: 0 50% 0 50%; }
}
<div id="box1">
</div>
<div id="box2">
</div>
Both methods should be immune to any timing issues due to tab being in background etc

2D Infinitely looping Array of elements

The Goal :
The idea is to create an element grid (image gallery for exemple) that would infinitely loop on itself scrolling on two axes.
There should be no holes nor too much randomness (avoid having the same element randomly falling aside from itself). And this no matter how many element there is in the first place (it seems easy to infinite loop through a grid of 16 (4*4) elements, not that much over 17 (17*1). (My guess is that any prime number of elements is by definition a pain to make a grid of).
So I actually found a wonderful working exemple :
http://www.benstockley.com/
It's actually really close (probably better) than what I was imagining. Now it's using canvas and i tried looking at the javascript and it's a 30000 minified lines long script so I really can't read any core logic behind it.
Math side / Problem solving :
This is the logic and theory behind the problem, the math involved and the mindset.
How the program should process the list of elements so we have no holes, infinite grid, best repartion of the elements over all the axes.
My guess is that it somehow has to be procedural. I'm not sure if we should create grids or loop through the list on every axes (kind of like sudoku ? i don't know);
Pratical side / UI / UX :
Any advice on the technologies involved, pieces of code. I'm guessing it classic DOM is out of the way and that somehow canvas or 2D webgl will be mandatory. But I would love to hear any advice on this side.
Besides all the elements grid processing. The UI and UX involved in exploring a 2D infinite or vast layout in DOM or renderer is somehow not classical. The best technologies or advice on doing this are welcome.
Exemples :
I would welcome any working exemple that somewhat share an aspect of this problem.
I've got a fiddle that's set up to arrange your 2d grid.
It functions by using horizontal and vertical "step sizes". So, moving one step right in the grid advances the horizontal step size in the list. Moving one step down advances the vertical step size in the list (and they accumulate).
We allow the advances in the list to loop back to zero when the end is reached.
It likely makes sense to use a horizontal step size of 1 (so a row of your grid will maintain your list order). For the vertical step size, you want an integer that shares no common divisors with the list length. Though it's no guarantee, I used the (rounded) square root of the list length as something that will work in lots of cases.
I'll reproduce the fiddle here:
var list = ['red','green','blue','cyan','orange','yellow','pink'];
var hstep = 1;
var vstep = Math.ceil(Math.sqrt(list.length));
function getListItem(x,y) {
var index = x * hstep + y * vstep;
return list[index % list.length];
}
var elementSize = 30;
var gutterSize = 10;
function getOffset(x,y) {
return [10 + (elementSize + gutterSize) * x, 10 + (elementSize + gutterSize) * y];
}
var frame = $('.frame');
function drawElement(x,y) {
var listItem = getListItem(x,y);
var offsets = getOffset(x,y);
var element = $('<div></div>').addClass('element').css({
left: offsets[0] + 'px',
top: offsets[1] + 'px',
'background-color': listItem
});
frame.append(element);
}
function drawElements() {
var x = 0, y = 0;
while (10 + (elementSize + gutterSize) * x < frame.width()) {
while (10 + (elementSize + gutterSize) * y < frame.height()) {
drawElement(x,y);
y++;
}
y = 0;
x++;
}
}
drawElements();
.frame {
border: 2px solid black;
margin: 40px auto;
height: 300px;
width: 300px;
position: relative;
overflow: hidden;
}
.frame .element {
position: absolute;
width: 30px;
height: 30px;
}
.buttons {
position: absolute;
top: 0px;
width: 100%;
}
.buttons button {
position: absolute;
width: 30px;
height: 30px;
padding: 5px;
}
button.up {top: 0px; left: 46%;}
button.down {top: 355px; left: 46%;}
button.left {top: 160px; left: 15px;}
button.right {top: 160px; right: 15px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame">
</div>
<div class="buttons">
<button class="up">↑</button>
<button class="down">↓</button>
<button class="left">←</button>
<button class="right">→</button>
</div>
You can see I've left some simple buttons to implement movement, but they are not functional yet. If you wanted to continue implementation along the lines of what I've done here, you could render your elements to a certain range beyond the visible frame, then implement some sort of animated repositioning. The renderElements function here only renders what is visible, so you can use something like that and not get stuck in rendering infinite elements, even though there's no theoretical limit to how far you can "scroll".
#arbuthnott I edited your code to implement the exploration via decrementing relativeX and relativeY variables. Also I inserted an "origin" div (1x1 px, overflow visible). This DOM element will represent the X and Y origin. I'm not sure it's essential but it's really convenient.
Now my function currently remove all elements and reinsert all elements on each update (every 500ms for now).
The idear would be to find a way to compare which elements I need versus which one already exists.
Maybe storing existing elements into an array, and compare the array with the "query" array. Than see just the elements that are missing.
This is the idear, not sure about the implementation (I suck at handling arrays).
https://jsfiddle.net/bnv6mumd/64/
var sources = ['red','green','blue','cyan','orange','yellow','pink','purple'];
var frame = $('.frame'),
origin = $('.origin');
var fWidth = 600,
fHeight = 300,
srcTotal = sources.length,
srcSquare = Math.ceil(Math.sqrt(srcTotal)),
rX = 0,
rY = 0;
var gridSize = 30,
gutterSize = 5,
elementSize = gridSize - gutterSize;
function getSourceItem(x,y) {
var index = x + y * srcSquare;
return sources[Math.abs(index) % srcTotal];
}
function getOffset(x,y) {
return [gridSize * x,gridSize * y];
}
function drawElement(x,y) {
var sourceItem = getSourceItem(x,y);
var offsets = getOffset(x,y);
var element = $('<div></div>').addClass('element').css({
left: offsets[0] + 'px',
top: offsets[1] + 'px',
'background-color': sourceItem,
});
origin.append(element);
}
function init() {
var x = 0, y = 0;
while ( gridSize * x < fWidth) {
while ( gridSize * y < fHeight) {
drawElement(x,y);
y++;
}
y = 0;
x++;
}
}
function updateElements() {
origin.empty();
var x = -Math.trunc(rX / gridSize) -1, y = - Math.trunc(rY / gridSize) -1;
while ( gridSize * x + rX < fWidth) {
while ( gridSize * y + rY < fHeight) {
drawElement(x,y);
y++;
}
y = -Math.ceil(rY / gridSize);
x++;
}
}
function animate() {
rX -= 5;
rY -= 5;
origin.css({left: rX, top: rY})
updateElements();
console.log("relative X : " + rX + " | relative Y : " + rY);
}
setInterval(animate, 500)
init();
.frame {
border: 2px solid black;
margin: 40px auto;
height: 300px;
width: 600px;
position: relative;
overflow: hidden;
}
.origin {
height: 1px;
width: 1px;
position: absolute;
overflow: visible;
}
.frame .element {
position: absolute;
width: 25px;
height: 25px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame">
<div class="origin" style="top:0;left:0;"></div>
</div>
This is my final snippet version (i will start to work on real implementation specific to my case now).
I think I optimized in a decent way DOM operations, code structure etc (I am very well open to suggestions though).
I now only update the elements that needs to be updated (click near the frame to show overflow)
https://jsfiddle.net/bnv6mumd/81/
var sources = ['red', 'green', 'blue', 'cyan', 'orange', 'yellow', 'pink', 'purple'];
var frame = $('.frame'),
origin = $('.origin');
var srcTotal = sources.length,
srcSquare = Math.round(Math.sqrt(srcTotal)),
fWidth = 200,
fHeight = 200,
cellSize = 50,
gutterSize = 20,
gridSize = [Math.floor(fWidth / cellSize) + 1, Math.floor(fHeight / cellSize) + 1],
aX = 0, // Absolute/Applied Coordinates
aY = 0,
rX = 0, // Relative/frame Coordinates
rY = 0;
function getSrcItem(x, y) {
var index = x + y * srcSquare;
return sources[Math.abs(index) % srcTotal];
}
function getOffset(x, y) {
return [cellSize * x, cellSize * y];
}
function getY() {
return Math.floor(-rY / cellSize);
}
function getX() {
return Math.floor(-rX / cellSize);
}
function drawElement(x, y) {
var srcItem = getSrcItem(x, y),
offsets = getOffset(x, y),
element = $('<div></div>').addClass('element').css({
left: offsets[0] + 'px',
top: offsets[1] + 'px',
'background-color': srcItem,
}).attr({
"X": x,
"Y": y
});
origin.append(element);
}
function drawCol(x, y) {
var maxY = y + gridSize[1];
while (y <= maxY + 1) {
drawElement(x - 1, y - 1);
y++;
}
}
function drawLign(x, y) {
var maxX = x + gridSize[0];
while (x <= maxX + 1) {
drawElement(x - 1, y - 1);
x++;
}
}
function drawGrid() {
origin.empty();
var x = getX(),
y = getY(),
maxX = x + gridSize[0],
maxY = y + gridSize[1];
while (y <= maxY + 1) {
drawLign(x, y);
x = getX();
y++;
}
}
function updateX(x, y, diffX, diffY) {
if (Math.sign(diffX) == -1) {
drawCol(aX - 1, y);
$('[x=' + (aX + gridSize[0]) + ']').remove();
aX--;
} else if (Math.sign(diffY) == 1) {
drawCol(aX + gridSize[0] + 2, y);
$('[x=' + (aX - 1) + ']').remove();
aX++;
}
}
function updateY(x, y, diffX, diffY) {
if (Math.sign(diffY) == -1) {
drawLign(x, aY - 1);
$('[y=' + (aY + gridSize[0]) + ']').remove();
aY--;
} else if (Math.sign(diffY) == 1) {
drawLign(x, aY + gridSize[0] + 2);
$('[y=' + (aY - 1) + ']').remove();
aY++;
}
}
function animate() {
rX += 1;
rY += 1;
origin.css({
left: rX,
top: rY
});
var x = getX(),
y = getY(),
diffX = x - aX,
diffY = y - aY;
if (diffX) {
updateX(x, y, diffX, diffY)
};
if (diffY) {
updateY(x, y, diffX, diffY)
};
requestAnimationFrame(animate);
}
$('body').click(function() {
$(frame).toggleClass("overflow");
})
drawGrid();
animate();
.frame {
border: 2px solid black;
margin: 100px auto;
height: 200px;
width: 200px;
position: relative;
}
.overflow{
overflow:hidden;
}
.origin {
height: 1px;
width: 1px;
position: absolute;
overflow: visible;
}
.frame .element {
position: absolute;
width: 30px;
height: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame overflow">
<div class="origin" style="top:0;left:0;"></div>
</div>

How to prevent element from exceeding his container borders

I have a square, when clicked it appears in a random location and it also change size (so for example if I have a 30px box but it is 10px from the left border I still get 10px outside the gamespace).
sometimes the square exceed his container border
How can I make sure that the square will never exceed his container?
function position() {
var positionTop = Math.random() * 100;
var positionLeft = Math.random() * 100;
var position = "position:relative;top:" + positionTop + "%;left:" + positionLeft + "%;"
return position;
}
document.getElementById("shape").onclick = function() {
document.getElementById("shape").style.cssText = position();
}
#gameSpace {
width: 100%;
height: 470px;
background: blue;
margin: 0;
padding-top: 30px;
}
#playSpace {
width: 90%;
height: 90%;
margin: 0 auto;
margin-top: 10px;
border: 1px black solid;
}
#shape {
width: 100px;
height: 100px;
background-color: red;
}
<div id="gameSpace">
<div id="playSpace">
<!-- here we put the shape -->
<div id="shape"></div>
</div>
</div>
You need to set position: relative; to the parent element and position: absolute; to the shape element. Then use max min value for random where max is the parent width/height and subtract the shape width/height ...
This is snippet before update
function position() {
var playSpace = document.querySelector('#playSpace');
var shape = document.getElementById("shape");
var maxHeight = playSpace.offsetHeight - shape.offsetHeight;
var maxWidth = playSpace.offsetWidth - shape.offsetWidth;
var positionTop = Math.random() * (maxHeight - 0) + 0;
var positionLeft = Math.random() * (maxWidth - 0) + 0;
// think of this like so:
// var positionTop = Math.random() * (max - min) + min;
// more information about it https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
var position = "position:absolute;top:" + positionTop + "px;left:" + positionLeft + "px;"
return position;
}
document.getElementById("shape").onclick = function() {
document.getElementById("shape").style.cssText = position();
}
#gameSpace {
width: 100%;
height: 470px;
background: blue;
margin:0;
padding-top: 30px;
}
#playSpace {
position: relative; /* add this line */
width: 90%;
height: 90%;
margin: 0 auto;
border: 1px black solid;
}
#shape {
width: 100px;
height: 100px;
background-color: red;
}
<div id="gameSpace">
<div id="playSpace">
<!-- here we put the shape -->
<div id="shape"></div>
</div>
</div>
Updated after comment
Not sure how you added the size() function but probably the problem was with using ...cssText that you overwrote the changes. So now I changed the code with passing the element to the functions and then only change the single CSS statements which need to be changed.
function position(element) {
var playSpace = document.querySelector('#playSpace');
var shape = document.getElementById("shape");
var maxHeight = playSpace.offsetHeight - shape.offsetHeight;
var maxWidth = playSpace.offsetWidth - shape.offsetWidth;
var positionTop = Math.random() * (maxHeight - 0) + 0;
var positionLeft = Math.random() * (maxWidth - 0) + 0;
// think of this like so:
// var positionTop = Math.random() * (max - min) + min;
// more information about it https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
element.style.position = 'absolute';
element.style.top = positionTop + "px";
element.style.left = positionLeft + "px";
}
function size(element) {
var sizeMath = (Math.random() * 200) + 50;
element.style.width = sizeMath + "px";
element.style.height = sizeMath + "px";
}
document.getElementById("shape").onclick = function() {
size(document.getElementById("shape"));
position(document.getElementById("shape"));
}
#gameSpace {
width: 100%;
height: 470px;
background: blue;
margin:0;
padding-top: 30px;
}
#playSpace {
position: relative; /* add this line */
width: 90%;
height: 90%;
margin: 0 auto;
border: 1px black solid;
}
#shape {
width: 100px;
height: 100px;
background-color: red;
}
<div id="gameSpace">
<div id="playSpace">
<!-- here we put the shape -->
<div id="shape"></div>
</div>
</div>
You can use a specify the range (min/max) in Math.random function and then use this function Math.floor(Math.random() * (max - min + 1)) + min; to limit the returned value of the random function between min and max.
maxTop : height of the container - height of shape
maxLeft : width of the container - width of shape
minTop : 0
minLeft : 0
You need to use position:absolute and px value on shape for this to work
See code snippet:
function position() {
var minTop = 0;
var maxTop = document.getElementById("playSpace").clientHeight - document.getElementById("shape").clientHeight;
var minLeft = 0;
var maxLeft = document.getElementById("playSpace").clientWidth - document.getElementById("shape").clientWidth ;
var positionTop = Math.floor(Math.random() * (maxTop - minTop + 1) + minTop);
var positionLeft = Math.floor(Math.random() * (maxLeft - minLeft + 1) + minLeft);
var position = "position:absolute;top:" + positionTop + "px;left:" + positionLeft + "px;"
return position;
}
document.getElementById("shape").onclick = function() {
document.getElementById("shape").style.cssText = position();
}
#gameSpace {
width: 100%;
height: 470px;
background: blue;
margin: 0;
padding-top: 30px;
text-align:center;
}
#playSpace {
width: 90%;
height: 90%;
border: 1px black solid;
position:relative;
display:inline-block;
}
#shape {
width: 100px;
height: 100px;
background-color: red;
}
<div id="gameSpace">
<div id="playSpace">
<!-- here we put the shape -->
<div id="shape"></div>
</div>
</div>
With CSS calc, limit the position inside playSpace area (you can use different units, here % and px).
Then with offsetTop/offsetLeft, get the real position of the square and avoid negative positions (when positionTop < 100px or positionLeft < 100px).
function position() {
var positionTop = Math.random() * 100;
var positionLeft = Math.random() * 100;
var position = "position:relative;top: calc(" + positionTop + "% - 100px);left: calc(" + positionLeft + "% - 100px);";
return position;
}
document.getElementById("shape").onclick = function() {
var shapeDiv = document.getElementById("shape");
shapeDiv.style.cssText = position();
var top = shapeDiv.offsetTop;// Result of calc(" + positionTop + "% - 100px) in px
if(top < 0) {
shapeDiv.style.top = '0px';
}
var left = shapeDiv.offsetLeft;// Result of calc(" + positionLeft + "% - 100px) in px
if(left < 0) {
shapeDiv.style.left = '0px';
}
}
Don't forget to add position: relative to #playSpace, to get offsetTop/left correct
#playSpace {
position:relative;/* mandatory */
}
You can get the parent element size, so that in your scirpt, use a condition to prevent the square to exceed.
function position() {
var maxWidth = document.getElementById("playSpace").offsetWidth;
var maxTop = document.getElementById("playSpace").offsetHeight;
var positionTop = Math.random() * 100;
var positionLeft = Math.random() * 100;
if (positionTop > maxTop) { positionTop = maxTop;)
if (positionLeft > maxWidth) { positionLeft = maxWidth;)
var position = "position:relative;top:" + positionTop + "%;left:" + positionLeft + "%;"
return position;
}
I'm not sure if .offsetWidth and .offsetHeight works well, but you got the idea :)
By tweaking just a little your positionTop and positionLeft variable, I managed to keep the square inside. You just need to change the maximum % that it needs to have to not exceed the container. I found that 75% for the top and 80% for the left seems to be the max!
var positionTop = Math.floor(Math.random() * 75) ;
var positionLeft = Math.random() * 80;
Here's a fiddle
If you set the containing element to position: relative; and the inner element with position: absolute; the inner elements top left right and bottom properties will calculate from the borders of the containing element.
Here is a good source of css positioning.
In your case additional validation should be set to positionTop and positionLeft.
positionTop should be in the interval [( shapeHeight/2 ) - (100 - shapeHeight/2 ) ]
positionLeft should be in the interval [( shapeWidth/2 ) - (100 - shapeWeight/2 ) ]

How to use % instead of px with draggable element?

I'm stucked and I need help. I'm making map with markers. I can add markers etc.
My main container have 100% width and height. When I click somewhere my marker has % values for example top: 34%; left: 35%;
After dragging marker new position have px value. I want to save % values.
Any ideas?
map.js
jQuery(document).ready(function($){
// basic add
$("button#remove").click(function(){
$(".marker").remove();
});
//add with position
var map = $(".map");
var pid = 0;
function AddPoint(x, y, maps) {
// $(maps).append('<div class="marker"></div>');
var marker = $('<div class="marker ui-widget-content"></div>');
marker.css({
"left": x,
"top": y
});
marker.attr("id", "point-" + pid++);
$(maps).append(marker)
$('.marker').draggable().resizable();
}
map.click(function (e) {
// var x = e.pageX - this.offsetLeft;
// var y = e.pageY - this.offsetTop;
var x = e.offsetX/ $(this).width() * 100 + '%';
var y = e.offsetY/ $(this).height() * 100 + '%';
// $(this).append('<div class="marker"></div>');
AddPoint(x, y, this);
});
// drag function
$(".ui-widget-content").draggable({
drag: function( event, ui ) {
var x = e.offsetX/ $(this).width() * 100 + '%';
var y = e.offsetY/ $(this).height() * 100 + '%';
}
});
});
style.css
.wrapper {
margin: 0 auto;
width: 100%;
min-height: 400px;
overflow: hidden;
}
.map {
width: 100%;
position: relative;
}
.map > img {
max-width: 100%;
max-height: 100%;
}
.marker {
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(255, 0, 0, 1);
position: absolute;
left: 50%;
top: 50%;
z-index: 999;
}
#mapa {
width: 30px;
height: 30px;
border-radius: 50%;
background: rgba(255, 0, 0, 1);
z-index: 999;
}
some html code
<div class="wrapper">
<div class="map">
<img src="http://i.imgur.com/HtxXGR5.jpg" width="100%" height="auto" margin="15px 0;" alt="">
</div>
<button id="remove">Remove all markers</button>
</div>
If you want to capture the position, after the drag is ended (fire 1 time per drag) you can do this:
$('.marker').draggable({
stop: function() {
var offset = $(this).offset();
var mapOffest = $('.map').offset();
var x = ((offset.left - mapOffest.left)/ ($(".map").width() / 100))+"%";
var y = ((offset.top - mapOffest.top)/ ($(".map").height() / 100))+"%";
// We need to do something with thoses vals uh?
$(this).css('left', x);
$(this).css('top', y);
// or
console.log('X:'+x + '||Y:'+y);
}
});
Fiddle here
Take note that the event has been set on the marker directy, this was probably your mistake.
The little calcul needs to take care of the map offset, which you don't need if your map is a full width/height (window size)
If you need, or prefer use the drag event rather than the stop, theses lines won't have any effects
$(this).css('left', x);
$(this).css('top', y);
But you'll still be able to capture the position, the console values will be goods.
Hope it may helps you.
problem solved, DFayet thank's again
final code
jQuery(document).ready(function($){
// basic add
$("button#remove").click(function(){
$(".marker").remove();
});
//add with position
var map = $(".map");
var pid = 0;
function AddPoint(x, y, maps) {
var marker = $('<div class="marker"></div>');
marker.css({
"left": x,
"top": y
});
marker.attr("id", "point-" + pid++);
$(maps).append(marker);
$('.marker').draggable({
stop: function(event, ui) {
var thisNew = $(this);
var x = (ui.position.left / thisNew.parent().width()) * 100 + '%';
var y = (ui.position.top / thisNew.parent().height()) * 100 + '%';
thisNew.css('left', x);
thisNew.css('top', y);
console.log(x, y);
}
});
}
map.click(function (e) {
var x = e.offsetX/ $(this).width() * 100 + '%';
var y = e.offsetY/ $(this).height() * 100 + '%';
AddPoint(x, y, this);
});
});

Categories