I have a random amount of boxes, randomly on a page of random colors. I am trying to be able to get them to move from one place to another.
Essentially, I am not familiar at all with mouse move events so this is quite the challenge. Even though it is quite simple.
Heres the code for the HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Ramdom Boxes</title>
<script src="A2Q1.js"></script>
</head>
<body>
</body>
</html>
Javascript:
window.onload = init;
function init() {
//when page is loaded create a bunch of boxes randomly throughout the page
//get the body element of the document
var body = document.getElementsByTagName("body")[0];
//store width and height of boxes
var boxWidth = 50;
var boxHeight = 50;
//create the random number for the boxes
var randNum = Math.floor(Math.random() * 500 + 1);
//create the boxes
for(var i=0;i<randNum;i++){
//create the random color and random positions
var colour = Math.round(0xffffff * Math.random()).toString(16);
var pos1 = Math.floor(Math.random() * window.innerWidth)
var pos2 = Math.floor(Math.random() * window.innerHeight)
// Define an array of css attributes
var attr =[
// Assign a colour to the box
'background-color:#' + colour,
// Place the box somewhere inside the window
'left:' + pos1 + 'px',
'top:' + pos2 + 'px',
// Set the box size
'width:' + boxWidth + 'px',
'height:' + boxHeight + 'px',
'cursor: pointer;',
'position:absolute;'
];
//join the attributes together
var attributes = attr.join(';');
//create a new div tag
var div = document.createElement("div");
//gives the box a unique id
div.setAttribute("id","box"+i)
//create the design of the box
div.setAttribute("style",attributes);
//add to the body
body.appendChild(div);
}
}
I really have no idea where to start...
Well a start would certainly be getting the mouse position, after that the world is your oyster.
var mousex = 0;
var mousey = 0;
function getXY(e){
if (!e) e = window.event;
if (e)
{
if (e.pageX || e.pageY)
{ // this doesn't work on IE6!! (works on FF,Moz,Opera7)
mousex = e.pageX;
mousey = e.pageY;
go = '[e.pageX]';
if (e.clientX || e.clientY) go += ' [e.clientX] '
}
else if (e.clientX || e.clientY)
{ // works on IE6,FF,Moz,Opera7
mousex = e.clientX + document.body.scrollLeft;
mousey = e.clientY + document.body.scrollTop;
go = '[e.clientX]';
if (e.pageX || e.pageY) go += ' [e.pageX] '
}
}
}
With the mouse info you can then do this in another function.
function moveBoxes(){
document.body.onmousemove = updater; //or some container div!
updater();
}
function updater(e){
getXY(e);
document.getElementById('aboxid').style.left=mousex+'px';
}
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
<head>
<title>MouseDown MouseUp</title>
<meta charset="UTF-8">
<style>
#insideBox{
height: 100px;
width: 100px;
background-color: black;
left:300px;
top:300px;
position:absolute;
}
</style>
<script>
var mouseFire = null;
window.onload = init;
function init(){
var div = document.getElementById("insideBox");
div.addEventListener("mousedown",mouseDrag,false);
}
function mouseDrag(e){
var evt = e || window.event;
mouseFire = evt.target || evt.srcElement;
document.addEventListener("mousemove",mouseMove,false);
document.addEventListener("mouseup",mouseDrop,false);
}
function mouseMove(e){
var evt = e || window.event;
var mouseX = evt.clientX;
var mouseY = evt.clientY;
mouseFire.style.left = mouseX-50+"px";
mouseFire.style.top = mouseY-50+"px";
}
function mouseDrop(e){
mouseFire = null;
document.removeEventListener("mousemove",mouseMove,false);
}
</script>
</head>
<body>
<div id="insideBox"></div>
</body>
</html>
Related
i am looking to have this cursor effect on the body:
https://www.screenshot-magazine.com/
i do have two separate codes, one for the bloc following the mouse and another to get the xy coords.
But i am too beginner to merge both together or make both work in parallel to have the XY coods printed in the box following my cursor moves.
Someone could help me?
Thanks a lot in advance:)
Anto
here the two codes:
1-bloc following mouse movements
<style>
#divtoshow {
position:absolute;
display:none;
color: #C0C0C0;
background-color: none;
}
<script type="text/javascript">
var divName = 'divtoshow'; // div that is to follow the mouse (must be position:absolute)
var offX = 15; // X offset from mouse position
var offY = 15; // Y offset from mouse position
function mouseX(evt) {if (!evt) evt = window.event; if (evt.pageX) return evt.pageX; else if (evt.clientX)return evt.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft); else return 0;}
function mouseY(evt) {if (!evt) evt = window.event; if (evt.pageY) return evt.pageY; else if (evt.clientY)return evt.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop); else return 0;}
function follow(evt) {
var obj = document.getElementById(divName).style;
obj.left = (parseInt(mouseX(evt))+offX) + 'px';
obj.top = (parseInt(mouseY(evt))+offY) + 'px';
}
document.onmousemove = follow;
</script>
<body>
<div id='onme' onMouseover='document.getElementById(divName).style.display="block"' onMouseout='document.getElementById(divName).style.display="none"'>
<div id="divtoshow">test</div>
2-get XY coords on body
<script>
function readMouseMove(e){
var result_x = document.getElementById('x_result');
var result_y = document.getElementById('y_result');
result_x.innerHTML = e.clientX;
result_y.innerHTML = e.clientY;
}
document.onmousemove = readMouseMove;
</script>
</head>
<body>
<h2 id="x_result">0</h2>
<h2 id="y_result">0</h3>
</body>
Your CSS is fine. You could have moved #x-result and #y-result into #divtoshow, and then set the left and right in readMouseMove. I've modified your code a bit. I'll explain it on the way
const xResult = document.getElementById("x-result");
const yResult = document.getElementById("y-result");
const divToShow = document.getElementById("divtoshow");
document.onmousemove = function (e) {
let mouse = {
x: e.clientX, // this is 2018, so you can just directly use clientX
y: e.clientY // and clientY
};
divToShow.style.left = `${mouse.x + 16}px`; // add a padding so that the text is not rendered directly below the mouse
divToShow.style.top = `${mouse.y + 16}px`;
xResult.innerHTML = `X: ${mouse.x}`; // write the text into the output divs
yResult.innerHTML = `Y: ${mouse.y}`;
};
#divtoshow {
color: #C0C0C0;
position: absolute;
}
<div id="divtoshow">
<span id="x-result">X: 0</span> <br> // i changed the h2s to spans because h2s have HUGE text
<span id="y-result">Y: 0</span>
</div>
You can add the #onme integration by yourself.
I have a code which displays a circle and an ellipse when I press "S" (the positions are chosen among 18 possible combinations). Then I have a while loop: I would like that, at every iteration, if the circle is touched a new position is generated right away and, if the circle is NOT touched for 3 consecutive times, a new position is generated.
I have 2 problems: 1) It seems that I cannot put the increment (positions ++) in the if/else statement only. 2) The second iteration does not update the positions of circle and ellipse (I should use a jQuery $ attribute?)
How can I solve this?
<html>
<head>
</head>
<link rel="stylesheet" href="cssFiles/blackBackground.css">
<script src="jsFiles/drawEllipse.js"></script>
<script src="jsFiles/drawCircle.js"></script>
<script src="jsFiles/newPosition.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<script src='https://code.responsivevoice.org/responsivevoice.js'></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<canvas id="myCanvas" width="1260" height="1000" style="border:1px solid red;"></canvas>
<p id="1"></p>
<p id="2"></p>
<script>
var ellipse = [[-150,-300],[-150,0],[-150,300],[150,-300],[150,0],[150,300]];
var circle = [[-350,-300],[-350,0],[-350,300],[350,-300],[350,0],[350,300]];
var radius = 95;
var positions = 0;
var clicks = 0;
var canvas = document.getElementById('myCanvas');
$(document).ready(function() {
document.addEventListener("keydown", function (e) {
if (e.keyCode === 83) { // "Start" is pressed
while (positions<6){
// generate first position
var idx = newPosition(ellipse,circle,radius)
var idx_ellipse = idx[0];
var idx_circle = idx[1];
var xRight = circle[idx_circle][0] + radius + canvas.width/2 ;
var xLeft = circle[idx_circle][0] - radius+ canvas.width/2;
var yTop = circle[idx_circle][1] - radius + canvas.height/2;
var yBottom = circle[idx_circle][1] + radius + canvas.height/2;
document.addEventListener("click", function(e) {
clicks++;
if (e.clientX< xRight && e.clientX> xLeft && e.clientY< yBottom && e.clientY> yTop) { //circle is correctly touched
var idx = newPosition(ellipse,circle,radius) // new position generated
positions++
}
else if (clicks > 3) {
var idx = newPosition(ellipse,circle,radius) // new position generated
positions++
}
}, false); //end of touch listener
} // end of while loop
} // end of "Start" keycode listener
}); //end of keydown listener
}) //end of document function
function newPosition(ellipse,circle,radius){
var idx_ellipse = Math.floor( Math.random() *6);
drawEllipse(ellipse[idx_ellipse][0],ellipse[idx_ellipse][1],radius,2.5,1);
// Check position of ellipse and draw circle on the other side
if (ellipse[idx_ellipse][0] == -150){
var idx_circle = Math.floor(Math.random() * (5 - 3 +1)) + 3;
drawCircle(circle[idx_circle][0],circle[idx_circle][1],radius,2.5,1);
}
else if(ellipse[idx_ellipse][0] == 150) {
var idx_circle = Math.floor(Math.random() * (2 - 0 +1)) + 0;
drawCircle(circle[idx_circle][0],circle[idx_circle][1],radius,2.5,1);
}
return [idx_ellipse,idx_circle];
}
</script>
</body>
</html>
For a project of big "text map" BigPicture, I need to have more than 1000 text inputs.
When you click + drag, you can "pan" the displayed area.
But the performance is very poor (both on Firefox and Chrome) : rendering 1000+ DOM elements is not fast at all.
Of course, another solution with better performance would be : work on a <canvas>, render text as bitmap on it, and each time we want to edit text, let's show a unique DOM <textarea>, that disappears what editing is finished, and text is rendered as bitmap again... It works (I'm currently working in this direction) but it needs much more code in order to provide editing on a canvas.
Question : Is it possible to improve performance for rendering of 1000+ DOM elements on a HTML page, so that I don't need to use <canvas> at all ?
Or will it be impossible to have good performance when panning a page with 1000+ DOM elements ?
Notes :
1) In the demo here I use <span contendteditable="true"> because I want multiline input + autoresize, but the rendering performance is the same with standard <textarea>.*
2) For reference, this is how I create the 1000 text elements.
for (i=0; i < 1000; i++)
{
var blax = (Math.random()-0.5)*3000;
var blay = (Math.random()-0.5)*3000;
var tb = document.createElement('span');
$(tb).data("x", blax / $(window).width());
$(tb).data("y", blay / $(window).height());
$(tb).data("size", 20 * currentzoom);
tb.contentEditable = true;
tb.style.fontFamily = 'arial';
tb.style.fontSize = '20px';
tb.style.position = 'absolute';
tb.style.top = blay + 'px';
tb.style.left = blax + 'px';
tb.innerHTML="newtext";
document.body.appendChild(tb);
}
For something like this you could make use of document fragment, these are DOM nodes that are not part of the actually DOM tree (more info can be found here https://developer.mozilla.org/en-US/docs/Web/API/document.createDocumentFragment), so you can do all your setup on the fragment and then append the fragment which will only be causing the one re flow rather than 1000.
So here is an example -http://jsfiddle.net/leighking2/awzoz7bj/ - a quick check on run time it takes around 60-70ms to run
var currentzoom = 1;
var docFragment = document.createDocumentFragment();
var start = new Date();
for (i=0; i < 1000; i++)
{
var blax = (Math.random()-0.5)*3000;
var blay = (Math.random()-0.5)*3000;
var tb = document.createElement('span');
$(tb).data("x", blax / $(window).width());
$(tb).data("y", blay / $(window).height());
$(tb).data("size", 20 * currentzoom);
tb.contentEditable = true;
tb.style.fontFamily = 'arial';
tb.style.fontSize = '20px';
tb.style.position = 'absolute';
tb.style.top = blay + 'px';
tb.style.left = blax + 'px';
tb.innerHTML="newtext";
docFragment.appendChild(tb);
}
document.body.appendChild(docFragment);
var end = new Date();
console.log(end-start)
compared to the original which took around 645ms to run http://jsfiddle.net/leighking2/896pusex/
UPDATE So for improving the dragging speed again keep the individual edits out of the DOM to avoid the cost of the reflow 1000 times every mouse drag
so here is one way using jquery's detach() method (example http://jsfiddle.net/sf72ubdt/). This will remove the elements from the DOM but give them to you with all their properties so you can manipulate them and reinsert them later on
redraw = function(resize) {
//detach spans
var spans = $("span").detach();
//now loop other them, because they are no longer attached to the DOM any changes are
//not going to cause a reflow of the page
$(spans).each(function(index) {
var newx = Math.floor(($(this).data("x") - currentx) / currentzoom * $(window).width());
var newy = Math.floor(($(this).data("y") - currenty) / currentzoom * $(window).height());
if (resize) {
displaysize = Math.floor($(this).data("size") / currentzoom);
if (displaysize) {
$(this).css({
fontSize: displaysize
});
$(this).show();
} else
$(this).hide();
}
//changed this from offset as I was getting a weird dispersing effect around the mouse
// also can no longer test for visible but i assume you want to move them all anyway.
$(this).css({
top: newy + 'px',
left: newx + 'px'
});
});
//reattach to the body
$("body").append(spans);
};
UPDATE 2 -
So to get a little more performance out of this you can cache the window width and height, use a vanilla for loop, use vanilla js to change the css of the span. Now each redraw (on chrome) takes around 30-45 ms (http://jsfiddle.net/leighking2/orpupsge/) compared to my above update which saw them at around 80-100ms (http://jsfiddle.net/leighking2/b68r2xeu/)
so here is the updated redraw
redraw = function (resize) {
var spans = $("span").detach();
var width = $(window).width();
var height = $(window).height();
for (var i = spans.length; i--;) {
var span = $(spans[i]);
var newx = Math.floor((span.data("x") - currentx) / currentzoom * width);
var newy = Math.floor((span.data("y") - currenty) / currentzoom * height);
if (resize) {
displaysize = Math.floor(span.data("size") / currentzoom);
if (displaysize) {
span.css({
fontSize: displaysize
});
span.show();
} else span.hide();
}
spans[i].style.top = newy + 'px',
spans[i].style.left = newx + 'px'
}
$("body").append(spans);
};
SNIPPET EXAMPLE -
var currentzoom = 1;
var docFragment = document.createDocumentFragment();
var start = new Date();
var positions = []
var end = new Date();
console.log(end - start);
var currentx = 0.0,
currenty = 0.0,
currentzoom = 1.0,
xold = 0,
yold = 0,
button = false;
for (i = 0; i < 1000; i++) {
var blax = (Math.random() - 0.5) * 3000;
var blay = (Math.random() - 0.5) * 3000;
var tb = document.createElement('span');
$(tb).data("x", blax / $(window).width());
$(tb).data("y", blay / $(window).height());
$(tb).data("size", 20 * currentzoom);
tb.contentEditable = true;
tb.style.fontFamily = 'arial';
tb.style.fontSize = '20px';
tb.style.position = 'absolute';
tb.style.top = blay + 'px';
tb.style.left = blax + 'px';
tb.innerHTML = "newtext";
docFragment.appendChild(tb);
}
document.body.appendChild(docFragment);
document.body.onclick = function (e) {
if (e.target.nodeName == 'SPAN') {
return;
}
var tb = document.createElement('span');
$(tb).data("x", currentx + e.clientX / $(window).width() * currentzoom);
$(tb).data("y", currenty + e.clientY / $(window).height() * currentzoom);
$(tb).data("size", 20 * currentzoom);
tb.contentEditable = true;
tb.style.fontFamily = 'arial';
tb.style.fontSize = '20px';
tb.style.backgroundColor = 'transparent';
tb.style.position = 'absolute';
tb.style.top = e.clientY + 'px';
tb.style.left = e.clientX + 'px';
document.body.appendChild(tb);
tb.focus();
};
document.body.onmousedown = function (e) {
button = true;
xold = e.clientX;
yold = e.clientY;
};
document.body.onmouseup = function (e) {
button = false;
};
redraw = function (resize) {
var start = new Date();
var spans = $("span").detach();
var width = $(window).width();
var height = $(window).height();
for (var i = spans.length; i--;) {
var span = $(spans[i]);
var newx = Math.floor((span.data("x") - currentx) / currentzoom * width);
var newy = Math.floor((span.data("y") - currenty) / currentzoom * height);
if (resize) {
displaysize = Math.floor(span.data("size") / currentzoom);
if (displaysize) {
span.css({
fontSize: displaysize
});
span.show();
} else span.hide();
}
spans[i].style.top = newy + 'px',
spans[i].style.left = newx + 'px'
}
$("body").append(spans);
var end = new Date();
console.log(end - start);
};
document.body.onmousemove = function (e) {
if (button) {
currentx += (xold - e.clientX) / $(window).width() * currentzoom;
currenty += (yold - e.clientY) / $(window).height() * currentzoom;
xold = e.clientX;
yold = e.clientY;
redraw(false);
}
};
$(function () {
$('body').on('mousedown', 'span', function (event) {
if (event.which == 3) {
$(this).remove()
}
})
});
zoomcoef = function (coef) {
middlex = currentx + currentzoom / 2
middley = currenty + currentzoom / 2
currentzoom *= coef
currentx = middlex - currentzoom / 2
currenty = middley - currentzoom / 2
redraw(true)
}
window.onkeydown = function (event) {
if (event.ctrlKey && event.keyCode == 61) {
zoomcoef(1 / 1.732);
event.preventDefault();
}
if (event.ctrlKey && event.keyCode == 169) {
zoomcoef(1.732);
event.preventDefault();
}
if (event.ctrlKey && event.keyCode == 48) {
zoomonwidget(1 / 1.732);
event.preventDefault();
}
};
html, body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
A solution was given by #Shmiddty which is much faster to all previous attempts : all elements should be wrapped, and only the wrapper has to be moved (instead of moving each element) :
http://jsfiddle.net/qhskacsw/
It runs smooth and fast even with 1000+ DOM elements.
var container = document.createElement("div"),
wrapper = document.createElement("div"),
dragging = false,
offset = {x:0, y:0},
previous = {x: 0, y:0};
container.style.position = "absolute";
wrapper.style.position = "relative";
container.appendChild(wrapper);
document.body.appendChild(container);
for (var i = 1000, span; i--;){
span = document.createElement("span");
span.textContent = "banana";
span.style.position = "absolute";
span.style.top = (Math.random() * 3000 - 1000 | 0) + 'px';
span.style.left = (Math.random() * 3000 - 1000 | 0) + 'px';
wrapper.appendChild(span);
}
// Don't attach events like this.
// I'm only doing it for this proof of concept.
window.ondragstart = function(e){
e.preventDefault();
}
window.onmousedown = function(e){
dragging = true;
previous = {x: e.pageX, y: e.pageY};
}
window.onmousemove = function(e){
if (dragging){
offset.x += e.pageX - previous.x;
offset.y += e.pageY - previous.y;
previous = {x: e.pageX, y: e.pageY};
container.style.top = offset.y + 'px';
container.style.left = offset.x + 'px';
}
}
window.onmouseup = function(){
dragging = false;
}
IMHO, I would go with your current thinking to maximize performance.
Reason: 1000+ DOM elements will always limit performance.
Yes, there is slightly more coding but your performance should be much better.
create one large offscreen canvas containing all 1000 texts.
Use context.textMeasure to calculate the bounding box of all 1000 texts relative to the image.
Save the info about each text in an object
var texts=[];
var texts[0]={ text:'text#0', x:100, y:100, width:35, height:20 }
...
context.drawImage that image on a canvas using an offset-X to 'pan' the image. This way you only have 1 canvas element instead of 1000 text elements.
In the mousedown handler, check if the mouse position is inside the bounding box of any text.
If the mouse is clicked inside a text bounding box, absolutely position an input-type-text directly over the text on the canvas. This way you only need 1 input element which can be reused for any of the 1000 texts.
Use the abilities of the input element to let the user edit the text. The canvas element has no native text editing abilities so don't "recreate the wheel" by coding canvas text editing.
When the user is done editing, recalculate the bounding box of the newly edited text and save it to the text object.
Redraw the offscreen canvas containing all 1000 texts with the newly edited text and draw it to the onscreen canvas.
Panning: if the user drags the onscreen canvas, draw the offscreen canvas onto the onscreen canvas with an offset equal to the distance the user has dragged the mouse. Panning is nearly instantaneous because drawing the offscreen canvas into the onscreen canvas-viewport is much, much faster than moving 1000 DOM input elements
[ Addition: full example with editing and panning ]
**Best Viewed In Full Screen Mode**
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var texts=[];
var fontSize=12;
var fontFace='arial';
var tcanvas=document.createElement("canvas");
var tctx=tcanvas.getContext("2d");
tctx.font=fontSize+'px '+fontFace;
tcanvas.width=3000;
tcanvas.height=3000;
var randomMaxX=tcanvas.width-40;
var randomMaxY=tcanvas.height-20;
var panX=-tcanvas.width/2;
var panY=-tcanvas.height/2;
var isDown=false;
var mx,my;
var textCount=1000;
for(var i=0;i<textCount;i++){
var text=(i+1000);
texts.push({
text:text,
x:parseInt(Math.random()*randomMaxX),
y:parseInt(Math.random()*randomMaxY)+20,
width:ctx.measureText(text).width,
height:fontSize+2,
});
}
var $textbox=$('#textbox');
$textbox.css('left',-200);
$textbox.blur(function(){
$textbox.css('left',-200);
var t=texts[$textbox.textsIndex]
t.text=$(this).val();
t.width=ctx.measureText(t.text).width;
textsToImage();
});
textsToImage();
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUpOut(e);});
$("#canvas").mouseout(function(e){handleMouseUpOut(e);});
// create one image from all texts[]
function textsToImage(){
tctx.clearRect(0,0,tcanvas.width,tcanvas.height);
for(var i=0;i<textCount;i++){
var t=texts[i];
tctx.fillText(t.text,t.x,t.y)
tctx.strokeRect(t.x,t.y-fontSize,t.width,t.height);
}
redraw();
}
function redraw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(tcanvas,panX,panY);
}
function handleMouseDown(e){
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
// is the mouse over a text?
var hit=false;
var x=mx-panX;
var y=my-panY;
for(var i=0;i<texts.length;i++){
var t=texts[i];
if(x>=t.x && x<=t.x+t.width && y>=t.y-fontSize && y<=t.y-fontSize+t.height){
$textbox.textsIndex=i;
$textbox.css({'width':t.width+5, 'left':t.x+panX, 'top':t.y+panY-fontSize});
$textbox.val(t.text);
$textbox.focus();
hit=true;
break;
}
}
// mouse is not over any text, so start panning
if(!hit){isDown=true;}
}
function handleMouseUpOut(e){
e.preventDefault();
e.stopPropagation();
isDown=false;
}
function handleMouseMove(e){
if(!isDown){return;}
e.preventDefault();
e.stopPropagation();
var mouseX=parseInt(e.clientX-offsetX);
var mouseY=parseInt(e.clientY-offsetY);
panX+=mouseX-mx;
panY+=mouseY-my;
mx=mouseX;
my=mouseY;
redraw();
}
body{ background-color: ivory; padding:10px; }
#wrapper{position:relative; border:1px solid blue; width:600px; height:600px;}
#textbox{position:absolute;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Click on #box to edit.<br>Tab to save changes.<br>Drag on non-text.</h4><br>
<div id=wrapper>
<input type=text id=textbox>
<canvas id="canvas" width=600 height=600></canvas>
</div>
<button></button>
I just run couple tests and it seems that moving absolutely positioned (position:absolute;) DOM elements (divs) with CSS transform:translate is even faster (by about 30%) than doing it via Canvas. But I was using CreateJS framework for the canvas job so my results may not hold for other use cases.
Is it possible to make a resizable and draggable div with a fixed surface area?
Example: I have a square div with a surface area of 10000px² (100 x 100px) and then I change the width (with the mouse on the corner or on the edge) to 50px, the heigth should now change automaticly to 200px so the surface area stays 10000px².
Then I´d like to drag it all over the page, resize it again etc...
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Draggable - Default functionality</title>
<link rel="stylesheet"
href="http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-
ui.css">
<script src="http://code.jquery.com/jquery-1.10.2.js"></script>
<script src="http://code.jquery.com/ui/1.10.4/jquery-
ui.js"></script>
<link rel="stylesheet" href="/resources/demos/style.css">
<style>
#draggable { width: 100px; height: 100px; padding: 0.5em; border:
1px solid;}
</style>
<script>
$(function() {
$( "#draggable" ).draggable().resizable();
});
</script>
</head>
<body>
<div id="draggable" class="ui-widget-content">
<p>Drag me around</p>
</div>
</body>
</html>
ok, so here is the code, I have resizable and draggable div but now I need the fixed surface area (eg 10000px²) as I mentioned before...
you can use jquery's UI to do all of this.
use http://jqueryui.com/draggable/ to allow for dragging,
use http://jqueryui.com/resizable/ to allow for resizing
using the resize event, you can have jquery modify the div so it will follow any height/width rules you want.
Try this:
(function(draggable) {
var elm = document.getElementById(draggable);
elm.onmousedown = drag;
function stop() {
document.onmouseup = null;
document.onmousemove = null;
}
function drag(e) {
if (e.target == elm) {
var absX = e.clientX;
var absY = e.clientY;
var left = parseInt(getComputedStyle(elm).left, 10);
var top = parseInt(getComputedStyle(elm).top, 10);
var relX = absX - left;
var relY = absY - top;
document.onmouseup = stop;
document.onmousemove = function(e) {
var absX = e.clientX;
var absY = e.clientY;
elm.style.left = absX - relX + "px";
elm.style.top = absY - relY + "px";
}
}
}
})("box");
(function(resizeable) {
var elm = document.getElementById(resizeable);
var wHandler = elm.childNodes[1];
var hHandler = elm.childNodes[3];
wHandler.onmousedown = resizeW;
hHandler.onmousedown = resizeH;
function stop() {
elm.style.cursor = "";
document.body.style.cursor = "";
document.onmouseup = null;
document.onmousemove = null;
}
var w = 100;
var h = 100;
var area = w * h;
function resizeW(e) {
elm.style.cursor = "w-resize";
document.body.style.cursor = "w-resize";
var left = parseInt(window.getComputedStyle(elm, null)["left"], 10);
document.onmouseup = stop;
document.onmousemove = function(e) {
var absX = e.clientX;
var width = absX - left;
var height = area / width;
elm.style.width = width + "px";
elm.style.height = height + "px";
}
}
function resizeH(e) {
elm.style.cursor = "n-resize";
document.body.style.cursor = "n-resize";
var top = parseInt(window.getComputedStyle(elm, null)["top"], 10);
document.onmouseup = stop;
document.onmousemove = function(e) {
var absY = e.clientY;
var height = absY - top;
var width = area / height;
elm.style.height = height + "px";
elm.style.width = width + "px";
}
}
})("box");
Fiddle
Though there's a small bug, I think. Couldn't fix it. When you resize the width it's ok, but when you resize the height, and then move the div, it lags or w/e. Check the fiddle. Other than this, it's working fine.
How can I get the image to be dragged around just if the mouse is clicked, not just follow the mouse around as it is doing right now.
<head>
<style>
#flying {
position: absolute;
}
</style>
<script type="text/javascript">
function UpdateFlyingObj (event) {
var mouseX = Math.round (event.clientX);
var mouseY = Math.round (event.clientY);
var flyingObj = document.getElementById ("flying");
flyingObj.style.left = mouseX + "px";
flyingObj.style.top = mouseY + "px";
}
this.onmouseup = function() {
document.onmousemove = null
}
</script>
</head>
<body onmousemove="UpdateFlyingObj (event);" >
<h1><center>Homework 13.7<center></h1>
<div style="height:1000px;"></div>
<img id="flying" src="flying.gif" />
</body>
The key-point that needs to be considered is handling the ondragstart event of the image. Failing to return false from it means that the browser absorbs the event and gives odd behaviour. See comment in the source.
Try this on for size. (dont forget to change the image-path)
<!DOCTYPE html>
<html>
<head>
<style>
#flying
{
position: absolute;
}
</style>
<script>
function byId(e){return document.getElementById(e);}
window.addEventListener('load', myInitFunc, false);
function myInitFunc()
{
byId('flying').addEventListener('mousedown', onImgMouseDown, false);
}
function onImgMouseDown(e)
{
e = e || event;
var mElem = this;
var initMx = e.pageX;
var initMy = e.pageY;
var initElemX = this.offsetLeft;
var initElemY = this.offsetTop;
var dx = initMx - initElemX;
var dy = initMy - initElemY;
document.onmousemove = function(e)
{
e = e || event;
mElem.style.left = e.pageX-dx+'px';
mElem.style.top = e.pageY-dy+'px';
}
this.onmouseup = function()
{
document.onmousemove = null;
}
// try to comment-out the below line
// doing so means the browser absorbs the ondragstart event and (in Chrome) drags a reduced-opacity copy of
// the image, overlaid with a circle that has a diagonal line through it. - just like the usuall behaviour for
// dragging an image on a webpage.
this.ondragstart = function() { return false; }
}
</script>
</head>
<body>
<h1><center>Homework 13.7<center></h1>
<img id="flying" src="img/opera.svg"/>
</body>
</html>