Translate += in jquery - javascript

Is there any easy way where we can add += for CSS.
var obj = $('#svg > g');
var x = parseInt(obj.attr('transform').split(/[()]/)[1].split(',')[0]);
var y = parseInt(obj.attr('transform').split(/[()]/)[1].split(',')[1]);
obj.attr('transform', 'translate(' + (x + 100) + ', ' + (y + 5) + ')');
$('#svg').css("width", "+=200");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="200">
<g transform="translate(10, 10)">
<rect x="0" y="0" rx="10" ry="10" width="100%" height="100%" pointer-events="all"></rect>
</g>
</svg>
Similar to styling the width. //css("width", "+=200");
i need for transform translate something like obj.attr('transform', 'translateX("+="'+100+')');.

You can use the jquery width attribute:
$('#svg').width($('#svg').width() + 200);
For the transforming the attribute, you can get the position
var x = $('#svg > g').position().left;
var y = $('#svg > g').position().top;
Then throw that to change the CSS of the object
$('#svg g').css({'transform': 'translate(' + (x + 100) +'px, ' + (y + 5) + 'px'});

Related

Drag and drop rotated images javascript

so i have a code that allows me to drag and drop an img tag. The drag and drop works fine but when i added a rotation function, the drag and drop started acting weird (the coordinates changed and when i drag the element the rotation reset). Also when i try dragging again, it goes back to its initial position, do you please have any idea on how i can fix this?
This is my code and thank you in advance:
let rotate=0
function rot_plus() {
rotate=rotate+10
$("#test").css('transform',"rotate("+rotate+"deg)")
}
function rot_minus() {
rotate=rotate-10
$("#test").css('transform',"rotate("+rotate+"deg)")
}
var active = false;
var currentX;
var currentY;
var initialX;
var initialY;
var xOffset = 0;
var yOffset = 0;
let current_elem
var container = document.querySelector("#boite");
container.addEventListener("mousedown", dragStart, false);
container.addEventListener("mouseup", dragEnd, false);
container.addEventListener("mousemove", drag, false);
function dragStart(e) {
if(e.target.id=="test"){
dragItem1=e.target.id
dragItem = document.querySelector("#"+e.target.id);
initialX=e.clientX-xOffset
initialY=e.clientY-yOffset
active = true;
}
}
function drag(e) {
if (active) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
setTranslate(currentX, currentY, dragItem);
}
}
function dragEnd(e) {
active = false;
initialX=currentX
initialY=currentY
selectedElement = null;
}
function setTranslate(xPos, yPos, el) {
el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0) rotate("+rotate+"deg)";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div id="boite">
<img src="https://static.vecteezy.com/system/resources/previews/009/342/282/original/cartoon-eyes-clipart-design-illustration-free-png.png" id="test" class="remove" style="position: absolute; width:150px; height:auto" >
<svg xmlns="http://www.w3.org/2000/svg" width="46" height="46" fill="currentColor" class="bi bi-plus-circle" id="rotplus" style="margin-top:120px" onclick="rot_plus()" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg"width="46" height="46" fill="currentColor" class="bi bi-dash-circle" id="rotminus"
style="margin-top:120px" onclick="rot_minus()" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
</svg>
</div>
</body>
</html>
You are using $.css to change the transform in the rotation functions. But this removes the positional changes as both are defined in 'transform'. That is, to fix this you need to keep the position information when rotating.
To do this it is better not to use jquery as it will clean the information in transform. So what I did was just replicate the line where you define the position but instead of taking the positions defined in your function I take it directly from the variables where you store the values.
I also used a CSS to prevent items from being selected when dragging:
CSS:
#boite,
#boite * {
user-select: none;
}
JS:
function rot_plus() {
const el = lastItemDragged
rotate = rotate + 10;
el.style.transform = "translate3d(" + xOffset + "px, " + yOffset + "px, 0) rotate(" + rotate + "deg)";
}
function rot_minus() {
const el = lastItemDragged
rotate = rotate - 10;
el.style.transform = "translate3d(" + xOffset + "px, " + yOffset + "px, 0) rotate(" + rotate + "deg)";
}
function setTranslate(el) {
el.style.transform = "translate3d(" + xOffset + "px, " + yOffset + "px, 0) rotate(" + rotate + "deg)";
}
I also added the variable lastItemDragged to store the last item dragged (so that the rotation reaches the same)
full code:
let rotate = 0
function rot_plus() {
const el = lastItemDragged
rotate = rotate + 10;
el.style.transform = "translate3d(" + xOffset + "px, " + yOffset + "px, 0) rotate(" + rotate + "deg)";
}
function rot_minus() {
const el = lastItemDragged
rotate = rotate - 10;
el.style.transform = "translate3d(" + xOffset + "px, " + yOffset + "px, 0) rotate(" + rotate + "deg)";
}
var lastItemDragged = document.querySelector('#test')
var active = false;
var currentX;
var currentY;
var initialX;
var initialY;
var xOffset = 0;
var yOffset = 0;
let current_elem
var container = document.querySelector("#boite");
container.addEventListener("mousedown", dragStart, false);
container.addEventListener("mouseup", dragEnd, false);
container.addEventListener("mousemove", drag, false);
function dragStart(e) {
if (e.target.id == "test") {
dragItem1 = e.target.id
dragItem = document.querySelector("#" + e.target.id);
initialX = e.clientX - xOffset
initialY = e.clientY - yOffset
active = true;
}
}
function drag(e) {
if (active) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
setTranslate(dragItem);
}
}
function dragEnd(e) {
active = false;
initialX = currentX
initialY = currentY
selectedElement = null;
}
function setTranslate(el) {
el.style.transform = "translate3d(" + xOffset + "px, " + yOffset + "px, 0) rotate(" + rotate + "deg)";
}
#boite,
#boite * {
user-select: none;
}
<div id="boite">
<img src="https://static.vecteezy.com/system/resources/previews/009/342/282/original/cartoon-eyes-clipart-design-illustration-free-png.png" id="test" class="remove" style="position: absolute; width:150px; height:auto">
<svg xmlns="http://www.w3.org/2000/svg" width="46" height="46" fill="currentColor" class="bi bi-plus-circle" id="rotplus" style="margin-top:120px" onclick="rot_plus()" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="46" height="46" fill="currentColor" class="bi bi-dash-circle" id="rotminus" style="margin-top:120px" onclick="rot_minus()" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z" />
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z" />
</svg>
</div>

Animating attribute values with JavaScript | Zooming in the SVG viewBox

We have a SVG grid generated with JavaScript.
The goal is to 2X zoom the SVG grid when the user double clicks on any coordinate on the grid and have a short animation transition between the previous zoom state and current zoom state. This actually works almost 100% fine in my snippet below except one problem:
I can animate the level of zoom but cannot smoothly animate the X and Y coordinate transitions well.
View the snippet below ( preferably in full screen ) and double click on the grid a few times.
'use strict'
function zoom( evt ){
var loc = getCoords( evt ),
newX = loc.x / 0.8 - 12.5,
newY = loc.y / 0.8 - 12.5,
grid = document.getElementById( 'grid' ),
viewBoxAttr = grid.getAttribute( 'viewBox' ),
viewBoxAry = viewBoxAttr.split( ' ' ),
curX = viewBoxAry[ 0 ], curY = viewBoxAry[ 1 ],
curZm = viewBoxAry[ 2 ], dblZm = curZm / 2,
tweenZm = curZm, diffX = 0,
interval = setInterval(
function(){
if( tweenZm >= dblZm ){
tweenZm = tweenZm / 1.015625;
diffX = newX - curX;
}
else {
clearInterval( interval );
}
zmOnPt( newX, newY, tweenZm );
},
10
),
ary = [];
ary.push( curZm );
ary.push( dblZm );
}
var grid = document.getElementById( 'grid' );
grid.addEventListener( 'dblclick', zoom );
createLines( '.h-lns' ); createLines( '.v-lns' );
createLabels( '.h-num' ); createLabels( '.v-num' );
recalibrate();
<head>
<link id="main" rel="stylesheet"
href="https://codepen.io/basement/pen/brJLLZ.css"
>
<link id="animations" rel="stylesheet"
href="https://codepen.io/basement/pen/zdXRWo.css"
>
</head>
<body id="body">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" class="cntr" id="grid">
<script id="injectGrid" xlink:href="https://codepen.io/basement/pen/brJLLZ.js">
</script>
<g id="drawing">
<circle cx="60" cy="40" r="0.5" fill="#0dd" opacity="0.9" />
<circle cx="70" cy="40" r="0.5" fill="#0dd" opacity="0.9" />
<path
fill="none" opacity="0.5" stroke="#0dd" stroke-width="0.5"
d="
M60, 40
A10, 10
0,
0, 1
70, 50
C70, 55
65, 60
60, 60
Q50, 60
50, 50
T55, 35
T70, 40
"
/>
</g>
</svg>
<script id="sidebar" src="https://codepen.io/basement/pen/zdXRWo.js"></script>
<script id="main" src="https://codepen.io/basement/pen/yorjXq.js"></script>
</body>
Notice the smooth zoom animation coupled with the jarring x and y translation? The viewBox just skips to the X and Y coordinate you clicked without animating over to it. Then it zooms in on the now centered coordinates.
The goal is for the x and y to transition smoothly with the zoom.
I've hidden a lot of the code I think is irrelevant in separate files this snippet links to on codepen. If you want to see those without copy and pasting the source code though, here is a list:
MAIN CSS:
https://codepen.io/basement/pen/brJLLZ.css
ANIMATION CSS:
https://codepen.io/basement/pen/zdXRWo.css
GRID CREATION JS:
https://codepen.io/basement/pen/brJLLZ.js
SIDEBAR CODE:
https://codepen.io/basement/pen/zdXRWo.js
MAIN JAVASCRIPT:
https://codepen.io/basement/pen/yorjXq.js
Your zoom function seems unnecessarily complicated. It has seemingly arbitrary equation constants that I don't understand, and you are manipulating coordinates in a way that I don't see a purpose for.
For the version below, I am just halving the viewBox width and height, then centering that on the coordinates where you click the mouse. Then, for the animation, I just do a linear interpolation from the old viewBox values to the new ones.
function zoom( evt ) {
var loc = getCoords( evt ),
grid = document.getElementById( 'grid' ),
viewBoxAttr = grid.getAttribute( 'viewBox' ),
viewBoxAry = viewBoxAttr.split(' ');
var oldX = parseFloat(viewBoxAry[0]);
var oldY = parseFloat(viewBoxAry[1]);
var oldWidth = parseFloat(viewBoxAry[2]);
var oldHeight = parseFloat(viewBoxAry[3]);
var newWidth = oldWidth / 2; // Halving the view width => zoom X2
var newHeight = oldHeight / 2;
var newX = loc.x - newWidth / 2;
var newY = loc.y - newHeight / 2;
var animProgress = 0; // Goes from 0 to 1
var animStep = 0.02; // Change in animProgress per interval function invocation.
var interval = setInterval( function() {
animProgress += animStep;
if (animProgress > 1)
animProgress = 1;
// Calculate a new viewBox corresponding to our animation progress
var nextViewBox = [
oldX + animProgress * (newX - oldX),
oldY + animProgress * (newY - oldY),
oldWidth + animProgress * (newWidth - oldWidth),
oldHeight + animProgress * (newHeight - oldHeight)
];
grid.setAttribute("viewBox", nextViewBox.join(' '));
if (animProgress >= 1)
clearInterval( interval );
}, 10);
}
'use strict'
function zoom( evt ) {
var loc = getCoords( evt ),
grid = document.getElementById( 'grid' ),
viewBoxAttr = grid.getAttribute( 'viewBox' ),
viewBoxAry = viewBoxAttr.split(' ');
var oldX = parseFloat(viewBoxAry[0]);
var oldY = parseFloat(viewBoxAry[1]);
var oldWidth = parseFloat(viewBoxAry[2]);
var oldHeight = parseFloat(viewBoxAry[3]);
var newWidth = oldWidth / 2;
var newHeight = oldHeight / 2;
var newX = loc.x - newWidth / 2;
var newY = loc.y - newHeight / 2;
var animProgress = 0; // Goes from 0 to 1
var animStep = 0.02; // Change in animProgress per interval function invocation.
var interval = setInterval( function() {
animProgress += animStep;
if (animProgress > 1)
animProgress = 1;
// Calculate a new viewBox corresponding to our animation progress
var nextViewBox = [
oldX + animProgress * (newX - oldX),
oldY + animProgress * (newY - oldY),
oldWidth + animProgress * (newWidth - oldWidth),
oldHeight + animProgress * (newHeight - oldHeight)
];
grid.setAttribute("viewBox", nextViewBox.join(' '));
if (animProgress >= 1)
clearInterval( interval );
}, 10);
}
var grid = document.getElementById( 'grid' );
grid.addEventListener( 'dblclick', zoom );
createLines( '.h-lns' ); createLines( '.v-lns' );
createLabels( '.h-num' ); createLabels( '.v-num' );
recalibrate();
<head>
<link id="main" rel="stylesheet"
href="https://codepen.io/basement/pen/brJLLZ.css"
>
<link id="animations" rel="stylesheet"
href="https://codepen.io/basement/pen/zdXRWo.css"
>
</head>
<body id="body">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" class="cntr" id="grid">
<script id="injectGrid" xlink:href="https://codepen.io/basement/pen/brJLLZ.js">
</script>
<g id="drawing">
<circle cx="60" cy="40" r="0.5" fill="#0dd" opacity="0.9" />
<circle cx="70" cy="40" r="0.5" fill="#0dd" opacity="0.9" />
<path
fill="none" opacity="0.5" stroke="#0dd" stroke-width="0.5"
d="
M60, 40
A10, 10
0,
0, 1
70, 50
C70, 55
65, 60
60, 60
Q50, 60
50, 50
T55, 35
T70, 40
"
/>
</g>
</svg>
<script id="sidebar" src="https://codepen.io/basement/pen/zdXRWo.js"></script>
<script id="main" src="https://codepen.io/basement/pen/yorjXq.js"></script>
</body>

Svg Draw flickering happening in Chrome browser

I need to allow user to draw a shape and I have used svg for same.
In other browser the code is working perfectly fine, but in case of chrome Version 52.0.2743.116 m it is flickering. When I created it it was working fine. But as chrome was updated the issue started happening
Issue is not replicable in chrome Version 49.0.2623.110 m
https://jsfiddle.net/2svxmgwu/1/
Try to drag and drop in yellow area from left top side to right bottom you will see barcode
Refer the attached image
The flickering does not happen if direction of creating shape is other then from top left to lower bottom.
Code
Html
<div class="thisComp">
<div class="svgElementDiv">
</div>
</div>
Js
var svgShape = $('<svg viewBox="0 0 640 480" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg"><g><title>Layer 1</title> <rect stroke="#5B9BD5" id="svg_11" height="473.99998" width="635.99997" y="3" x="3" stroke-linecap="null" stroke-linejoin="null" preserveAspectRatio="none" vector-effect="non-scaling-stroke" stroke-dasharray="null" stroke-width="3" fill="#5B9BD5"/> </g></svg>');
var self = this;
//Added for chrome browser as without this flickering is very high
svgShape.find('[vector-effect="non-scaling-stroke"]').removeAttr('vector-effect');
var $ShapeWrapper = $(".svgElementDiv");
var $thisComp = $(".thisComp");
svgShape.css({
position: "absolute",
left: "0",
top: "0",
width: "100%",
height: "100%",
opacity: "0.5"
});
$ShapeWrapper.css({
position: "fixed"
});
$thisComp.mousedown(function (event) {
if ($ShapeWrapper.length > 0) {
$ShapeWrapper.html(svgShape);
$ShapeWrapper.css("left", event.clientX);
$ShapeWrapper.css("top", event.clientY);
self._selectorLeftPos = event.clientX;
self._selectorTopPos = event.clientY;
$ShapeWrapper.css({
width: "0",
height: "0"
});
self._dragged = false;
event.preventDefault();
}
});
var removeShape = function (event) {
if ($ShapeWrapper.find(svgShape).length > 0 && !(event.type=="mouseleave" && $(event.relatedTarget).closest('.guideWrapper').length>0)) {
var startX = (($ShapeWrapper.offset().left - $thisComp.offset().left) / self._transformRatio) / self._conversionFactor;
var startY = (($ShapeWrapper.offset().top - $thisComp.offset().top) / self._transformRatio) / self._conversionFactor;
var selectWidth = ($ShapeWrapper.width() / self._transformRatio) / self._conversionFactor;
var selectHeight = ($ShapeWrapper.height() / self._transformRatio) / self._conversionFactor;
self._shapeData = '{"left":"' + startX + '","top":"' + startY + '","height":"' + selectHeight + '","width":"' + selectWidth + '"}';
svgShape.remove();
}
}
$thisComp.mousemove(function (event) {
if ($ShapeWrapper.length > 0) {
var width = 0;
var height = 0;
if (event.clientX <= self._selectorLeftPos) {
width = $ShapeWrapper.offset().left - event.clientX + $ShapeWrapper.width();
$ShapeWrapper.css("left", event.clientX);
}
else {
width = event.clientX - $ShapeWrapper.offset().left;
}
if (event.clientY <= self._selectorTopPos) {
height = $ShapeWrapper.offset().top - event.clientY + $ShapeWrapper.height();
$ShapeWrapper.css("top", event.clientY);
}
else {
height = event.clientY - $ShapeWrapper.offset().top;
}
$ShapeWrapper.css("width", width);
$ShapeWrapper.css("height", height);
if (width > 3 || height > 3) {
self._dragged = true;
}
}
});
$thisComp.bind("mouseup", removeShape);
$thisComp.bind("mouseleave", removeShape);
Css
.thisComp{
height:400px;
width:400px;
background-color:yellow;
}

Element no longer draggable after adding parameter to d3 drag behavior

The following code creates a circle and makes it draggable:
HTML:
<svg width='400' height='400'>
<rect width='100%' height='100%'></rect>
<circle transform='translate(259.5,197)' r='10'></circle>
</svg>
JS:
var drag = d3.behavior.drag().on('drag', dragMove)
d3.select('svg').append('circle').attr('transform', 'translate(' + '100px' + ',' + '100px' + ')').attr('r', '5').call(drag)
function dragMove(test) {
var x = d3.event.x
var y = d3.event.y
console.log(test)
d3.select(this).attr('transform', 'translate(' + '200px' + ',' + '200px' + ')')
}
It works. But when I add an argument to dragMove:
'drag', function() { dragMove('test') }
The dragging functionality stops working (console.log(test) outputs 'test', though)
Why is this happening and how to fix it?
https://jsfiddle.net/alexcheninfo/d8doyc9r/4/
It should be this way:
var drag = d3.behavior.drag().on('drag', function(d) {
dragMove(this);//pass the this
})
function dragMove(me) {
var x = d3.event.x
var y = d3.event.y
//operate on me
d3.select(me).attr('transform', 'translate(' + x + ',' + y + ')')
}
working code here
Hope this helps!

jQuery offset() does not work in Safari / Chrome iOS

The following code loops through an array of images and creates them based on the position of Highcharts marker ( a text that is associated with a point on a chart ). It works great in Chrome, FF, IE on desktop but does not work in Safar both Desktop or iOS and even Chrome iOS does not show the images.
The error I am getting is : main.js:453 - TypeError: 'undefined' is not an object (evaluating 'markerOffset.left')
$.each(value, function(k, v){
var z = i;
// Store the current marker
var marker = $('text').filter(function() { if($(this).html() === k) { return $(this) } });
// Get the markers offset
var markerOffset = marker.offset();
// If in Firefox, set the marker height to 13 px
if(marker.height() == 0){
markerHeight = 13;
}
else{
markerHeight = marker.height();
}
// Get the image dimensions
// Create new image object and get the width and height
// The image has to be downloaded first
var img = new Image();
// Set the image location
img.src = v.img;
// When the image is downloaded, you can get the dimensions
img.onload = function(){
var imgHeight = img.height;
var imgDivHeight = img.height;
var imgWidth = img.width;
// If the image Width is more than 90px, resize it
if(imgWidth > 50){
imgDivHeight = imgHeight / (imgWidth / 50);
imgHeight = (imgHeight / (imgWidth / 50)) + 5;
imgWidth = 50;
}
// Create the offset values based on the image sizes
**// THIS IS LINE 453**
var imgLeft = markerOffset.left - ((imgWidth - marker[0].getComputedTextLength()) / 2);
var imgTop = markerOffset.top - (imgHeight - (markerHeight / 4));
// Create an element for each value.img and make it position absolute and set the offset of the marker
$('.charts-inner').prepend('<div class="series-data series-picture-wrapper ' + hidden + ' image-on-chart" data-id="' + k + '" data-series="' + key + '" data-position-top="' + (imgTop - $('.app-ui-top').height() - (markerHeight)) + '" data-position-left="' + (imgLeft - ($('.app-ui-menu').width() + 3)) + '" data-hidden="' + hiddenAttr + '" style="z-index: ' + (5555 + z) + '; top:' + (imgTop - $('.app-ui-top').height() - (markerHeight)) + 'px; left:' + (imgLeft - ($('.app-ui-menu').width() + 3)) + 'px; width:' + imgWidth + 'px; height:' + imgDivHeight + 'px"><img src="' + v.img + '" style="width:' + imgWidth + 'px" /><div class="app-tooltip-stock nodisplay">' + v.tooltip + '</div></div>');
}
})
Right, so the problem was not the OFFSET but that $('text') is an SVG element and in Safari you cant get its content by html().
I changed the filter part to
var marker = $('text').filter(function() {
if($(this)[0].textContent === k) { return $(this) }
});
and it is working now...

Categories