Using Mouse Drag To Control Background Position - javascript

I want to use the 'mouse's drag' to drag a background's position around, inside a box.
The CSS:
.filmmakers #map {
width : 920px;
height : 500px;
margin-top : 50px;
margin-left : 38px;
border : 1px solid rgb(0, 0, 0);
cursor : move;
overflow : hidden;
background-image : url('WorldMap.png');
background-repeat : no-repeat;
}
The html:
<div id = "map" src = "WorldMap.png" onmousedown = "MouseMove(this)"> </div>
The Javascript:
function MouseMove (e) {
var x = e.clientX;
var y = e.clientY;
e.style.backgroundPositionX = x + 'px';
e.style.backgroundPositionY = y + 'px';
e.style.cursor = "move";
}
Nothing happens, no errors, no warnings, nothing... I have tried lots of things: an absolutely positioned image inside a div (you can guess why that didn't work), A draggable div inside a div with a background image, a table with drag and drop, and finally I tried this:
function MouseMove () {
e.style.backgroundPositionX = 10 + 'px';
e.style.backgroundPositionY = 10 + 'px';
e.style.cursor = "move";
}
This works, but its not relative to the mouse's position, pageX and pageY don't work either.
A live demo: http://jsfiddle.net/VRvUB/224/
P.S: whatever your idea is, please don't write it in JQuery

From your question I understood you needed help implementing the actual "dragging" behavior. I guess not. Anyway, here's the results of my efforts: http://jsfiddle.net/joplomacedo/VRvUB/236/
The drag only happens when the mouse button, and.. well, it behaves as I think you might want it to. Just see the fiddle if you haven't =)
Here's the code for those who want to see it here:
var AttachDragTo = (function () {
var _AttachDragTo = function (el) {
this.el = el;
this.mouse_is_down = false;
this.init();
};
_AttachDragTo.prototype = {
onMousemove: function (e) {
if ( !this.mouse_is_down ) return;
var tg = e.target,
x = e.clientX,
y = e.clientY;
tg.style.backgroundPositionX = x - this.origin_x + this.origin_bg_pos_x + 'px';
tg.style.backgroundPositionY = y - this.origin_y + this.origin_bg_pos_y + 'px';
},
onMousedown: function(e) {
this.mouse_is_down = true;
this.origin_x = e.clientX;
this.origin_y = e.clientY;
},
onMouseup: function(e) {
var tg = e.target,
styles = getComputedStyle(tg);
this.mouse_is_down = false;
this.origin_bg_pos_x = parseInt(styles.getPropertyValue('background-position-x'), 10);
this.origin_bg_pos_y = parseInt(styles.getPropertyValue('background-position-y'), 10);
},
init: function () {
var styles = getComputedStyle(this.el);
this.origin_bg_pos_x = parseInt(styles.getPropertyValue('background-position-x'), 10);
this.origin_bg_pos_y = parseInt(styles.getPropertyValue('background-position-y'), 10);
//attach events
this.el.addEventListener('mousedown', this.onMousedown.bind(this), false);
this.el.addEventListener('mouseup', this.onMouseup.bind(this), false);
this.el.addEventListener('mousemove', this.onMousemove.bind(this), false);
}
};
return function ( el ) {
new _AttachDragTo(el);
};
})();
/*** IMPLEMENTATION ***/
//1. Get your element.
var map = document.getElementById('map');
//2. Attach the drag.
AttachDragTo(map);
​

This isn't working because you are passing the element "map" to your MouseMove function, and using it as both an event object and an element. You can fix this painlessly by using JavaScript to assign your event handler rather than HTML attributes:
<div id="map"></div>
And in your JavaScript:
document.getElementById('map').onmousemove = function (e) {
// the first parameter (e) is automatically assigned an event object
var x = e.clientX;
var y = e.clientY;
// The context of this is the "map" element
this.style.backgroundPositionX = x + 'px';
this.style.backgroundPositionY = y + 'px';
}
http://jsfiddle.net/VRvUB/229/
The downside of this approach is that the backgroundPositionX and backgroundPositionY style properties are not supported in all browsers.
You mention "an absolutely positioned image inside a div" which is probably the more compatible solution for this. To make this setup work, you need to set the position of the outer element to relative, which makes absolute child elements use its bounds as zero.
<div id="map">
<img src="" alt="">
</div>
CSS:
#map {
position:relative;
overflow:hidden;
}
#map img {
position:absolute;
left:0;
top:0;
}
Here it is applied to your code: http://jsfiddle.net/VRvUB/232/

This works 100%
Vanilla Javascript
document.getElementById('image').onmousemove = function (e) {
var x = e.clientX;
var y = e.clientY;
this.style.backgroundPositionX = -x + 'px';
this.style.backgroundPositionY = -y + 'px';
this.style.backgroundImage = 'url(https://i.ibb.co/vhL5kH2/image-14.png)';
}
document.getElementById('image').onmouseleave = function (e) {
this.style.backgroundPositionX = 0 + 'px';
this.style.backgroundPositionY = 0 + 'px';
this.style.backgroundImage = 'url(https://i.ibb.co/Ph9MCB2/template.png)';
}
.container {
max-width: 670px;
height: 377px;
}
#image {
max-width: 670px;
height: 377px;
cursor: crosshair;
overflow: hidden;
background-image: url('https://i.ibb.co/Ph9MCB2/template.png');
background-repeat: no-repeat;
}
<div class="container">
<div id="image">
</div>
</div>

Related

Using two mouse events after each other

I am creating a canvas alike without using canvas tag , by creating new div each time mouse is down, i can't figure out how to run a second event.
Like mousedown then mousemove event where the seconed event occur only after the first one is true?
also if you can help with the offset coordinates
var paintbox = document.getElementById("canvas");
var start = function() {
paintbox.addEventListener("mousedown", drawOnCanvas);
};
var newColor = document.getElementById("colorPick");
var drawOnCanvas = function() {
var newClick = document.createElement("div");
newClick.setAttribute("id", "smallDiv");
newClick.style.backgroundColor = newColor.value;
newClick.style.width = "10px";
newClick.style.height = "10px";
newClick.style.position = "absolute";
paintbox.appendChild(newClick);
}
Set the handler on the mousemove event instead of mousedown and in the handler check whether the mouse button is down.
It would be better to move the 10px/position style settings in a CSS class. Also, don't generate elements with the same id attribute value: that generates invalid HTML. Use a class instead.
For the positioning you can use the pageX and pageY properties of the event object.
Finally, as the events will be triggered on the small divs when you make small moves, an extra check might be necessary to verify the mouse is still in the paint box.
var paintbox = document.getElementById("canvas");
var start = function() {
paintbox.addEventListener("mousemove", drawOnCanvas);
};
var newColor = document.getElementById("colorPick");
var drawOnCanvas = function(e) {
if ((e.buttons & 1) === 0) return; // Mouse button is not down
// Extra check to see we are well within the box boundaries:
var box = paintbox.getBoundingClientRect();
if (e.clientX - 5 < box.left || e.clientX + 5 > box.right
|| e.clientY - 5 < box.top || e.clientY + 5 > box.bottom) return;
var newClick = document.createElement("div");
newClick.className = "smallDiv"; // Don't create duplicate ID; put CSS in class
newClick.style.backgroundColor = newColor.value;
paintbox.appendChild(newClick);
newClick.style.left = (e.pageX-5) + "px";
newClick.style.top = (e.pageY-5) + "px";
}
start();
#canvas {
height: 150px;
width: 300px;
border: 1px solid;
display: inline-block;
margin: 10px;
}
.smallDiv {
width: 10px;
height: 10px;
position: absolute;
}
Color: <input id="colorPick" type="color"><br>
<div id="canvas"></div>

Get the focused image element inside a contenteditable div

I need to modify a pasted image inside a contenteditable div: resize it proportionately, add borders, etc... Perhaps this can be achieved by adding a className that will alter the necessary CSS properties.
The problem is that I don't know how to reference the focused, i.e. the active, the clicked-upon image or any element for that matter.
This is the HTML that I am trying to use
<!DOCTYPE html>
<html>
<body>
<div contenteditable="true">This is a div. It is editable. Try to change this text.</div>
</body>
</html>
Using css, html and javascript
Your editable content should be inside a parent div with an id or class name, you can even have different divs for images and such.
Then it is as simple as having css like so:
#editableContentDiv img {
imgProperty: value;
}
Then you can have javascript like so:
function insertImage(){
var $img = document.querySelector("#editableContentDiv img");
$img.setAttribute('class','resize-drag');
}
Eventually if you have more than one img inside the same div you can use querySelectorAll instead of querySelector and then iterate through the img tags inside same div.
I beleive that should at least give you an idea of where to start with what you want.
Similar example
I found this gist that seems to have similar things to want you want but a bit more complicated.
function resizeMoveListener(event) {
var target = event.target,
x = (parseFloat(target.dataset.x) || 0),
y = (parseFloat(target.dataset.y) || 0);
// update the element's style
target.style.width = event.rect.width + 'px';
target.style.height = event.rect.height + 'px';
// translate when resizing from top or left edges
x += event.deltaRect.left;
y += event.deltaRect.top;
updateTranslate(target, x, y);
}
function dragMoveListener(event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.dataset.x) || 0) + event.dx,
y = (parseFloat(target.dataset.y) || 0) + event.dy;
updateTranslate(target, x, y);
}
function updateTranslate(target, x, y) {
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
// update the position attributes
target.dataset.x = x;
target.dataset.y = y;
}
function insertImage() {
var $img = document.createElement('img');
$img.setAttribute('src', 'https://vignette.wikia.nocookie.net/googology/images/f/f3/Test.jpeg/revision/latest?cb=20180121032443');
$img.setAttribute('class', 'resize-drag');
document.querySelector(".container-wrap").appendChild($img);
var rect = document.querySelector(".container-wrap").getBoundingClientRect();
$img.style.left = rect.left;
$img.style.top = rect.top;
}
function dataTransfer() {
//cleanup
var $out = document.querySelector(".out-container-wrap");
while ($out.hasChildNodes()) {
$out.removeChild($out.lastChild);
}
//get source
var source = document.querySelector('.container-wrap')
//get data
var data = getSource(source);
//add data to target
setSource($out, data);
}
/**
* Get data from source div
*/
function getSource(source) {
var images = source.querySelectorAll('img');
var text = source.querySelector('div').textContent;
//build the js object and return it.
var data = {};
data.text = text;
data.image = [];
for (var i = 0; i < images.length; i++) {
var img = {}
img.url = images[i].src
img.x = images[i].dataset.x;
img.y = images[i].dataset.y;
img.h = images[i].height;
img.w = images[i].width;
data.image.push(img)
}
return data;
}
function setSource(target, data) {
//set the images.
for (var i = 0; i < data.image.length; i++) {
var d = data.image[i];
//build a new image
var $img = document.createElement('img');
$img.src = d.url;
$img.setAttribute('class', 'resize-drag');
$img.width = d.w;
$img.height = d.h;
$img.dataset.x = d.x;
$img.dataset.y = d.y;
var rect = target.getBoundingClientRect();
$img.style.left = parseInt(rect.left);
$img.style.top = parseInt(rect.top);
//transform: translate(82px, 52px)
$img.style.webkitTransform = $img.style.transform = 'translate(' + $img.dataset.x + 'px,' + $img.dataset.y + 'px)';
//$img.style.setProperty('-webkit-transform', 'translate('+$img.dataset.x+'px,'+$img.dataset.y+'px)');
target.appendChild($img);
}
//make a fresh div with text content
var $outContent = document.createElement('div')
$outContent.setAttribute('class', 'out-container-content');
$outContent.setAttribute('contenteditable', 'true');
$outContent.textContent = data.text;
target.appendChild($outContent);
}
interact('.resize-drag')
.draggable({
onmove: dragMoveListener,
inertia: true,
restrict: {
restriction: "parent",
endOnly: true,
elementRect: {
top: 0,
left: 0,
bottom: 1,
right: 1
}
}
})
.resizable({
edges: {
left: true,
right: true,
bottom: true,
top: true
},
onmove: resizeMoveListener
})
.resize-drag {
z-index: 200;
position: absolute;
border: 2px dashed #ccc;
}
.out-container-content,
.container-content {
background-color: #fafcaa;
height: 340px;
}
#btnInsertImage {
margin-bottom: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="http://code.interactjs.io/interact-1.2.4.min.js"></script>
</head>
<body>
<button id="btnInsertImage" onclick="insertImage()">Insert Image</button>
<div class="container-wrap">
<div class="container-content" contenteditable="true">This is the content of the container. The content is provided along with the image. The image will be moved around and resized as required. The image can move within the boundary of the container.</div>
</div>
<button id="btnSubmit" onclick="dataTransfer()">Submit</button>
<div class="out-container-wrap">
</div>
</body>
</html>
Source
This is what I tried and it seems to be working - at least on my system, the snippet I made does not work unfortunately.
function getSelImg(){
var curObj;
window.document.execCommand('CreateLink',false, 'http://imageselectionhack/');
allLinks = window.document.getElementsByTagName('A');
for(i = 0; i < allLinks.length; i++)
{
if (allLinks[i].href == 'http://imageselectionhack/')
{
curObj = allLinks[i].firstChild;
window.document.execCommand('Undo'); // undo link
break;
}
}
if ((curObj) && (curObj.tagName=='IMG'))
{
//do what you want to...
curObj.style.border = "thick solid #0000FF";
}
}
<!DOCTYPE html>
<html>
<body>
<input type="button" onclick="getSelImg()" value="set img border">
<div contenteditable="true">This is a div.<br>
It is editable.<br>
Try to paste an image here:<br>
###########################<br>
<br>
<br>
<br>
###########################
</div>
</body>
</html>

Jump/scroll to #id within a scroll element [duplicate]

I have a div that has overflow: scroll and I have some elements inside the DIV that are hidden. On click of a button on the page, I want to make the DIV scroll to a particular element inside the DIV.
How do I achieve this?
You need to read the offsetTop property of the div you need to scroll to and then set that offset to the scrollTop property of the container div. Bind this function the event you want to :
function scrollToElementD(){
var topPos = document.getElementById('inner-element').offsetTop;
document.getElementById('container').scrollTop = topPos-10;
}
div {
height: 200px;
width: 100px;
border: 1px solid black;
overflow: auto;
}
p {
height: 80px;
background: blue;
}
#inner-element {
background: red;
}
<div id="container"><p>A</p><p>B</p><p>C</p><p id="inner-element">D</p><p>E</p><p>F</p></div>
<button onclick="scrollToElementD()">SCROLL TO D</button>
function scrollToElementD(){
var topPos = document.getElementById('inner-element').offsetTop;
document.getElementById('container').scrollTop = topPos-10;
}
Fiddle : http://jsfiddle.net/p3kar5bb/322/ (courtesy #rofrischmann)
Just improved it by setting a smooth auto scrolling inside a list contained in a div
https://codepen.io/rebosante/pen/eENYBv
var topPos = elem.offsetTop
document.getElementById('mybutton').onclick = function () {
console.log('click')
scrollTo(document.getElementById('container'), topPos-10, 600);
}
function scrollTo(element, to, duration) {
var start = element.scrollTop,
change = to - start,
currentTime = 0,
increment = 20;
var animateScroll = function(){
currentTime += increment;
var val = Math.easeInOutQuad(currentTime, start, change, duration);
element.scrollTop = val;
if(currentTime < duration) {
setTimeout(animateScroll, increment);
}
};
animateScroll();
}
//t = current time
//b = start value
//c = change in value
//d = duration
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
I guess it may help someone :)
Here's a simple pure JavaScript solution that works for a target Number (value for scrollTop), target DOM element, or some special String cases:
/**
* target - target to scroll to (DOM element, scrollTop Number, 'top', or 'bottom'
* containerEl - DOM element for the container with scrollbars
*/
var scrollToTarget = function(target, containerEl) {
// Moved up here for readability:
var isElement = target && target.nodeType === 1,
isNumber = Object.prototype.toString.call(target) === '[object Number]';
if (isElement) {
containerEl.scrollTop = target.offsetTop;
} else if (isNumber) {
containerEl.scrollTop = target;
} else if (target === 'bottom') {
containerEl.scrollTop = containerEl.scrollHeight - containerEl.offsetHeight;
} else if (target === 'top') {
containerEl.scrollTop = 0;
}
};
And here are some examples of usage:
// Scroll to the top
var scrollableDiv = document.getElementById('scrollable_div');
scrollToTarget('top', scrollableDiv);
or
// Scroll to 200px from the top
var scrollableDiv = document.getElementById('scrollable_div');
scrollToTarget(200, scrollableDiv);
or
// Scroll to targetElement
var scrollableDiv = document.getElementById('scrollable_div');
var targetElement= document.getElementById('target_element');
scrollToTarget(targetElement, scrollableDiv);
You need a ref to the div you wish to scroll to inner-div and a ref to the scrollable div scrollable-div:
const scrollToDiv = () => {
const innerDivPos = document.getElementById('inner-div').offsetTop
document
.getElementById('scrollable-div')
.scrollTo({ top: innerDivPos, behavior: 'smooth' })
}
<div id="scrollable-div" style="height:100px; overflow-y:auto;">
<button type="button" style="margin-bottom:500px" onclick="scrollToDiv()">Scroll To Div</button>
<div id="inner-div">Inner Div</div>
</div>

How to transfer events into the canvas { pointer-events : none }; (!in KonvaJS)

The essence of the title is described and presented in my example.
My task is to make the pseudo shape. You need to hover on the canvas element (triangle), canvas accepted property {pointer-events:all}, and the care with this element {pointer-events:none}. How this can be done using the framework konvajs.
/*NON GIST*/
var stage=new Konva.Stage({container:'container',width:300,height:300})
,layer=new Konva.Layer()
,triangle=new Konva.RegularPolygon({x:80,y:120,sides:3,radius:80,fill:'#00D2FF',stroke:'black',strokeWidth:4})
,text=new Konva.Text({x:10,y:10,fontFamily:'Calibri',fontSize:24,text:'',fill:'black'});
function writeMessage(message){text.setText(message);layer.draw();}
/*GIST*/
triangle.on('mouseout', function() {
$('#container').css('pointer-events',/*!*/'none');
writeMessage('Mouseout triangle');
});
/*! How do I know if the events are not tracked on the canvas?*/
triangle.on('mousemove', function() {
$('#container').css('pointer-events',/*!*/'all');
var mousePos = stage.getPointerPosition();
var x = mousePos.x - 190;
var y = mousePos.y - 40;
writeMessage('x: ' + x + ', y: ' + y);
});
/*/GIST/*/
layer.add(triangle);
layer.add(text);
stage.add(layer);
body{
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;}
#container{
position:absolute;
z-index:1;
}
.lower-dom-element{
position:absolute;
z-index:0;
padding:20px;
background:#0e0;
top: 90px;
left: 0px;}
<script src="https://cdn.rawgit.com/konvajs/konva/0.9.0/konva.min.js"></script>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<div id="container"></div>
<div class="lower-dom-element">
If the POINTER-EVENTS on me, then canvas POINTER-EVENTS:NONE, and vice versa.
If the events are not on the triangle, then the event with me.
</div>
PS: sorry for my english.
var $con = $('#container');
$(document.body).mousemove(function(event) {
var x = event.clientX, y = event.clientY;
var offset = $con.offset();
// manually check intersection
if (layer.getIntersection({x: x - offset.left, y: y - offset.top})){
$con.css('pointer-events',/*!*/'all');
} else {
$con.css('pointer-events',/*!*/'none');
}
});
Demo: http://jsfiddle.net/vfp22dye/3/

Draggable Columns With Pure JavaScript

I'm trying to build a draggable column based layout in JavaScript and having a bit of hard time with it.
The layout comprises of 3 columns (divs), with two dragable divs splitting each. The idea is that they are positioned absolutely and as you drag the draggers, the columns' respective widths, and left values are updated.
The three columns should always span the full width of the browser (the right most column is 100% width), but the other two should remain static by default when the browser is resized (which is why i'm using px, not %).
My code isn't working as of yet, I'm relatively new to JavaScript (which is why I don't want to use jQuery).
Having said that, there must be a more efficient (and cleaner) way of achieving this with less code that works (without reaching for the $ key).
If anyone with some awesome JS skills can help me out on this I'd be super-appreciative.
Here's the fiddle I'm working on http://jsfiddle.net/ZFwz5/3/
And here's the code:
HTML
<!-- colums -->
<div class="col colA"></div>
<div class="col colB"></div>
<div class="col colC"></div>
<!-- draggers -->
<div class="drag dragA" style="position: absolute; width: 0px; height: 100%; cursor: col-resize; left:100px;"><div></div></div>
<div class="drag dragB" style="position: absolute; width: 0px; height: 100%; cursor: col-resize; left: 300px;"><div></div></div>
CSS:
body {
overflow:hidden;
}
.col {
position: absolute;
height:100%;
left: 0;
top: 0;
overflow: hidden;
}
.colA {background:red;width:100px;}
.colB {background:green; width:200px; left:100px;}
.colC {background:blue; width:100%; left:300px;}
.drag > div {
background: 0 0;
position: absolute;
width: 10px;
height: 100%;
cursor: col-resize;
left: -5px;
}
and my terrible JavaScript:
//variabe columns
var colA = document.querySelector('.colA');
var colB = document.querySelector('.colB');
var colC = document.querySelector('.colC');
//variable draggers
var draggers = document.querySelectorAll('.drag');
var dragA = document.querySelector(".dragA");
var dragB = document.querySelector(".dragB");
var dragging = false;
function drag() {
var dragLoop;
var t = this;
var max;
var min;
if (dragging = true) {
if (this == dragA) {
min = 0;
max = dragB.style.left;
} else {
min = dragA.style.left;
max = window.innerWidth;
}
dragLoop = setInterval(function () {
var mouseX = event.clientX;
var mouseY = event.clientY;
if (mouseX >= max) {
mouseX = max;
}
if (mouseY <= min) {
mouseY = min;
}
t.style.left = mouseX;
updateLayout();
}, 200);
}
}
function updateLayout() {
var posA = dragA.style.left;
var posB = dragB.style.left;
colB.style.paddingRight = 0;
colA.style.width = posA;
colB.style.left = posA;
colB.style.width = posB - posA;
colC.style.left = posB;
colC.style.width = window.innerWidth - posB;
}
for (var i = 0; i < draggers.length; i++) {
draggers[i].addEventListener('mousedown', function () {
dragging = true;
});
draggers[i].addEventListener('mouseup', function () {
clearInterval(dragLoop);
dragging = false;
});
draggers[i].addEventListener('mouseMove', function () {
updateLayout();
drag();
});
}
I see a couple of things wrong here. First of all, the mousemove event only fires on an element when the mouse is over that element. You might have better luck registering a mousemove listener on the parent of your div.drag elements, then calculating the mouse's position inside that parent whenever a mouse event happens, then using that position to resize your columns and your draggers.
Second, I'm not quite sure what you're trying to do by registering a function with setInterval. You're doing pretty well with registering event listeners; why not continue to use them to change the state of your DOM? Why switch to a polling-based mechanism? (and the function you pass to setInterval won't work anyway - it refers to a variable named event, which in that context is undefined.)
This is just a little example... I hope it can help you :)
window.onload = function() {
var myDiv = document.getElementById('myDiv');
function show_coords(){
var monitor = document.getElementById('monitor');
var x = event.clientX - myDiv.clientWidth / 2;
var y = event.clientY - myDiv.clientWidth / 2;
monitor.innerText = "X: " + x + "\n" + "Y: " + y;
myDiv.style.left = x + "px";
myDiv.style.top = y + "px";
}
document.onmousemove = function(){
if(myDiv.innerText == "YES"){show_coords();}
}
myDiv.onmousedown = function(){
myDiv.innerText = "YES";
}
myDiv.onmouseup = function(){
myDiv.innerText = "NO";
}
}

Categories