Rotate svg image from center after moving - javascript

I have made a basic svg image resizing, rotating and dragging inside mask on fiddle. I'm getting trouble in rotate image from center after moving. How do I reset origin x,y after moving?
Please move the image first, then rotate it with slider.
Try here: https://jsfiddle.net/david7418/o497akje/6/
Transform attribute gets overwrited here. How do I get origin x,y and put into transform arguments?
//Slider rotate
$( "#slider_rotate" ).slider({
max: 360,
min: 0,
step:1,
value:0,
slide: function( event, ui ) {
var matrixObj = obj.transform().localMatrix.split()
console.log(matrixObj);
var t = new Snap.Matrix();
//Transform overwrite
t.add(obj.transform().localMatrix);
obj.transform('r'+ui.value+','+t);
}
});
Full code:
$(function() {
obj = Snap(".cat");
//Enable drag event
obj.drag();
var dimens = obj.node.getBoundingClientRect();
var orinDimens = dimens;
//Image max enlarge rate
var enlargeRate = 4;
var smax = dimens.width;
//Slider resize
$( "#slider_resize" ).slider({
max: smax*enlargeRate,
min: smax,
step:1,
value:dimens.width,
slide: function( event, ui ) {
w = ui.value * dimens.width / smax;
h = ui.value * dimens.height / smax;
obj.attr({
width: w,
height: h,
x: (dimens.width - w)/2,
y: (dimens.height - h)/2
});
}
});
//Slider rotate
$( "#slider_rotate" ).slider({
max: 360,
min: 0,
step:1,
value:0,
slide: function( event, ui ) {
obj.transform('r'+ui.value);
}
});
//Reset button
$('#reset').click(function(){
$("#slider_resize").slider('value',orinDimens.width);
$("#slider_rotate").slider('value',0);
var objMatrix = new Snap.Matrix();
objMatrix.translate(0,0);
objMatrix.rotate(0);
obj.transform(objMatrix);
obj.attr({width: orinDimens.width,x:0})
});
});

I would create one central place to update all the transforms. It flows a bit easier probably if you use a Snap plugin as well for ease of coding, so something like the following.
We will store the x,y,r,s variables in a data() method for the object.
Snap.plugin( function( Snap, Element, Paper, global ) {
Element.prototype.updateTransform = function() {
var newTransform = 't' + (this.data('xy').x) + ',' + (this.data('xy').y);
newTransform += 'r' + (this.data('r'))
newTransform += 's' + (this.data('s'))
this.transform( newTransform )
return this;
}
Element.prototype.init = function() {
return this.data('xy', {x:0,y:0})
.data('r',0)
.data('s',1)
}
});
Now we have an update method, we can call this, any time we change something on the screen, or want to initialise it or whatever.
So when we want to reset it all, we can call...
$('#reset').click(function(){
obj.init().updateTransform();
});
For the drag handler...(store the old position in oxy)
function dragStart (x,y) {
this.data('oxy', this.data('xy') );
}
function dragMove( dx,dy,x,y ) {
this.data('xy', { x: this.data('oxy').x + dx ,
y: this.data('oxy').y + dy })
.updateTransform();
}
And for the sliders, we just store the values, and call updateTransform()
$( "#slider_resize" ).slider({
max: 4,
min: 1,
step:0.1,
slide: function( event, ui ) {
obj.data('s', ui.value)
.updateTransform();
}
});
With it all tied together, it would look like...
jsfiddle

Related

Getting the left and top positions with a draggable control

I have few components on my page which are draggable. They have the class
draggable. When this is dragged and stops I need to get the left and top positions
function init() {
var p = $('#groups-parent');
$('.draggable').draggable({
obstacle: ".obstacle",
preventCollision: true,
containment: [
p.offset().left, p.offset().top, p.offset().left + p.width() - 225, p.offset().top + p.height() - 50
],
start: function(event, ui) {
$(this).removeClass('obstacle');
},
stop: function (event, ui) {
console.log(ui);
$('#groups-parent').css({ 'min-height': Math.max(500, ui.position.top + 50) });
$(this).addClass('obstacle');
getTopLeftPosition();
}
});
}
Here is my function that should get the left and top positions.
function getTopLeftPosition(e) {
var coordX = e.pageX - $(this).offset().left,
coordY = e.pageY - $(this).offset().top;
alert(coordX + ' , ' + coordY);
}
This does not work. All I get in my browser is this error message
Cannot read properties of undefined (reading 'left')
How can I get this left and top positions? The alert should indicate it.
You probably just need to bind this to the function, by changing
getTopLeftPosition();
to either
getTopLeftPosition.call(this, event);
or
getTopLeftPosition.bind(this)(event)

How to show value of Jquery slider into multiple dropped divs

I'm using jQuery UI slider and drag and drop to create a way of specifying a rating out of 100 for each div.
The problem is when I drag my divs onto the slider, I do not know how to get the value for the position of each div on the slider. Here is a plnk + some code;
http://plnkr.co/edit/wSS2gZnSeJrvoBNDK6L3?p=preview
$(init);
function init() {
var range = 100;
var sliderDiv = $('#ratingBar');
sliderDiv.slider({
min: 0,
max: range,
slide: function(event, ui) {
$(".slider-value").html(ui.value);
}
});
var divs = '.itemContainer'
$(divs).draggable({
containment: '#content',
cursor: 'move',
snap: '#ratingBar',
revert: function(event, ui) {
$(this).data("uiDraggable").originalPosition = {
top: 0,
left: 0
};
return !event;
}
});
var position = sliderDiv.position(),
sliderWidth = sliderDiv.width(),
minX = position.left,
maxX = minX + sliderWidth,
tickSize = sliderWidth / range;
$('#ratingBar').droppable({
tolerance: 'touch',
drop: function(e, ui) {
var finalMidPosition = $(ui.draggable).position().left + Math.round($(divs).width() / 2);
if (finalMidPosition >= minX && finalMidPosition <= maxX) {
var val = Math.round((finalMidPosition - minX) / tickSize);
sliderDiv.slider("value", val);
$(".slider-value").html(ui.value);
}
}
});
$(".slider-value").html(sliderDiv.slider('value'));
}
Hope someone can offer some advice,
Cheers
(Also, if someone knows why I can drop the divs outside of the rating bar on either side, please let me know!)
Jquery UI has a function called stop and there is where you want to handle all the calculations as such:
stop: function(event, ui) {
// Object dragged
var e = ui.helper;
// Offset of that object
var eOffset = e.offset().left;
// Sliders offset
var sliderOffset = sliderDiv.offset().left;
// Subtract their offsets
var totalOffset = eOffset - sliderOffset;
// Width of box dragged
var boxWidth = ui.helper.width();
// Subtract their widths to account for overflow on end
var sliderW = sliderDiv.width() - boxWidth;
// Get percent moved
var percent = totalOffset / sliderW * 100;
// Find .slider-value and replace HTML
e.find(".slider-value").html(Math.round(percent));
}
This will be located here:
$(divs).draggable({
containment: '#content',
cursor: 'move',
snap: '#ratingBar',
revert: function(event, ui) {
$(this).data("uiDraggable").originalPosition = {
top: 0,
left: 0
};
return !event;
},
.... <-- HERE
});
I have provided commenting for every line so you understand what I did.
Above your draggable function you need to define a tighter containment area as such:
var left = sliderDiv.offset().left;
var right = left + sliderDiv.width();
var top = sliderDiv.offset().top;
var bottom = top + sliderDiv.height();
var height = $(".itemContainer").height();
var width = $(".itemContainer").width();
$(divs).draggable({
containment: [left, 0, right - width, bottom - height],
.....
});
This will prevent the draggable from being moved left, right, or below the slider.
Basically you grab the position of the object grabbed, adjust for some offset, adjust for some width, calculate the percentage, and replace the html (Could use text() as well).
Here is your plunker redone: http://plnkr.co/edit/3WSCo77c1cC5uFiYO8bV?p=preview
Documentation:
Jquery UI
Jquery .find

Control html5 canvas animation speed by jquery ui slider

I'm trying to build a interactive html5 canvas animation. so i'm trying to control animation speed based on jquery ui slider value.
when slider value changes automatically it will reflect in canvas animation.
i'm beginner in jquery and canvas animation, so kindly describe possible way in code samples or reference materials.
Here is my link for codepen
$(function() {
$( "#slider" ).slider({
orientation: "vertical",
range: "min",
min: 0,
max: 100,
value: 60,
slide: function( event, ui ) {
$( "#amount" ).val( ui.value );
}
});
$( "#amount" ).val( $( "#slider" ).slider( "value" ) );
});
//canvas animation
var canvas = document.getElementById("stage");
var ctx = canvas.getContext("2d");
var speed= 2; // get jquery ui slider value
The important thing:
clearInterval(t);
t = setInterval(DrawCanvas, 120 / ui.value);
Copy pastable code:
<div class="container">
<div class="btn"></div>
<input id="amount"type="text" />
<div id="slider" style="height:200px;"></div>
<canvas id="stage" width="289" height="289" style="border:2px solid black;top: 20%; left: 50%;" ></canvas>
</div>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/themes/smoothness/jquery-ui.css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>
<script>
var t; // this will be the setInterval object. We will control the speed with this
var factor = 120; // 120ms / the slider value; default: 120/4 = 30
//ui slider
$( "#slider" ).slider({
orientation: "vertical",
range: "min",
min: 1,
max: 10,
value: 4,
slide: function( event, ui ) {
$( "#amount" ).val( ui.value );
clearInterval(t);
t = setInterval(DrawCanvas, 120 / ui.value);
}
});
$( "#amount" ).val( $( "#slider" ).slider( "value" ) );
var speedvar= document.getElementById("amount").value;
var canvas = document.getElementById("stage");
var ctx = canvas.getContext("2d");
var speed= speedvar; //get value form jquery ui slider
var Ball = new Array();
var BallNow;
var SumMove = 50;
var posX, posY, r, TheAngle, TheSpeed, MoveX, MoveY;
for(i=0; i<SumMove; i++) {
posX = r + Math.random() * (canvas.width - r*2);
posY = r + Math.random() * (canvas.height - r*2);
r = 5;
TheAngle = Math.random() * 360;
TheSpeed = speed;
console.log(TheSpeed);
MoveX = Math.cos(Math.PI/180 * TheAngle) * TheSpeed;
MoveY = Math.sin(Math.PI/180 * TheAngle) * TheSpeed;
BallNow = {x: posX, y: posY, r:r, MoveX: MoveX, MoveY: MoveY};
Ball.push(BallNow);
}
t = setInterval(DrawCanvas, 30);
function DrawCanvas() {
ctx.clearRect(0,0,canvas.width,canvas.height);
for(i=0; i<SumMove; i++) {
Ball[i].x += Ball[i].MoveX;
Ball[i].y += Ball[i].MoveY;
if(Ball[i].x + Ball[i].r >= canvas.width || Ball[i].x - Ball[i].r <= 0) {
Ball[i].MoveX = -Ball[i].MoveX;
}
if(Ball[i].y + Ball[i].r >= canvas.height || Ball[i].y - Ball[i].r <= 0) {
Ball[i].MoveY = -Ball[i].MoveY;
}
ctx.beginPath();
ctx.fillStyle = "#042fcf";
ctx.arc(Ball[i].x, Ball[i].y, Ball[i].r, 0, Math.PI*2,false);
ctx.fill();
ctx.closePath();
}
}
</script>
instead of using setInterval to animate the canvas... Try using setTimeout inside your animation function and set the speed in between frames to your slider value.
What you have:
setInterval(DrawCanvas,30);
function DrawCanvas(){}
what you should try:
function DrawCanvas(){
setTimeout(DrawCanvas, speed);
}
DrawCanvas();
Basically, setTimeout will run just once, and after set time, so you can change the time between frames dynamically.
On the other hand, setInterval will run automatically every set time frame, so you cannot change its timing without stopping it first.

Jquery Knob not animating with %

I'm trying to animate my jquery dial and have the libraries loaded and all yet when I try to animate them after building the knob object, nothing happens. If I call both $('#dial') on both the animate and knob constructor it animates but doesn't ceil the value or give it a percent until I click it after its finished animating.
Here is what I have got
HTML
<input type="text" value="0" class="dial"/>
Javascript
var dial = $('.dial').knob({
value: 0,
width: 120,
height: 120,
min: 0,
max: 100,
stopper: true,
readOnly: true,//if true This will Set the Knob readonly cannot click
draw: function () {
$(this.i).val(this.cv + '%'); //Puts a percent after values
},
'format': function(v) {return v + '%'},
thickness: 0.1,
tickColorizeValues: true,
skin: 'tron'
});
dial.animate({//animate to data targetValue
value: 89
}, {
duration: 5000,
easing: 'swing',
step: function () {
var percent = Math.ceil(this.value) + '%';
$(this).val(Math.ceil(this.value) + '%').trigger('change');
// $(this).val($(this).val() + '%');
}
});
here my code maybe help you (included animation and style )
http://codepen.io/AdelDima/pen/ueKrI
I used GSAP animation library as there seemed to be a conflict with step and progress callbacks from the jquery animate.

Resize image which uses Jcrop

I use the Jquery Jcrop for cropping my images. Now I'm implementing a slider for resizing the image. I want the cropping and resizing to happend on the same page.
I do it like this:
$(document).ready(function() {
var img = $('#cropbox')[0]; // Get my img elem
var orgwidth, orgheight;
$("<img/>") // Make in memory copy of image to avoid css issues
.attr("src", $(img).attr("src"))
.load(function() {
orgwidth = this.width; // Note: $(this).width() will not
orgheight = this.height; // work for in memory images.
});
$('#cropbox').Jcrop({
onSelect: updateCoords
});
$("#imageslider").slider({
value: 100,
max: 100,
min: 1,
slide: function(event, ui) {
//$('ul#grid li').css('font-size',ui.value+"px");
resizeImage(orgwidth, orgheight);
}
});
});
And my simple resizeImage function:
function resizeImage(orgwidth, orgheight) {
var value = $('#imageslider').slider('option', 'value');
var width = orgwidth * (value / 100);
var height = orgheight * (value / 100);
$('#cropbox').width(width);
$('#cropbox').height(height);
$('#tester').val("org: "+orgwidth+" now: "+width);
}
The problem is that, as soon I turn on Jcrop I can't resize the image. How can I use both these functions at the same time?
I ended up destroying the jCrop while resizing and putting it back on after resize. Thanks anyway. Code:
function resizeImage(orgwidth, orgheight) {
jcrop_api.destroy();
var value = $('#imageslider').slider('option', 'value');
var width = orgwidth * (value / 100);
var height = orgheight * (value / 100);
$('#cropbox').width(width);
$('#cropbox').height(height);
$('#rw').val(width);
$('#rh').val(height);
initJcrop();
}
I had the same task to accomplish: resize the image with a slider where jCrop is applied. There are some more elements you have to resize also that jCrop created, not only the image. I ended up patching the jCrop plugin and here is the patch for latest jCrop-0.9.10.
Patch your jCrop. If you don't know how to apply the patch, just put the resizeImage function to line 1578 of jCrop (unimified version ofcourse):
--- /home/dr0bz/Desktop/jquery.Jcrop.js
+++ /home/dr0bz/workspace/profile_tuning/js/lib/jquery.Jcrop.js
## -1573,6 +1573,15 ##
ui: {
holder: $div,
selection: $sel
+ },
+
+ resizeImage: function(width, height) {
+ boundx = width;
+ boundy = height;
+ $([$img2, $img, $div, $trk]).each(function(index, element)
+ {
+ element.width(width).height(height);
+ });
}
};
Get the jCrop API:
var jCropApi;
$('#photo').Jcrop({}, function()
{
jCropApi = this;
});
Calc new height and width. If your are doing it with a slider, let the slider say return the new width of the image and you calculate new height with aspect ratio of the image:
var aspectRatio = width / height;
// newWidth returned by slider
var newHeight = Math.round(width / aspectRatio);
jCropApi.resizeImage(newWidth, newHeight);
There are some other points to keep an eye on. After each resize your should look that crop area is still in the viewport of the image. If you need i could post the complete source how i've done it for me: jCrop + jquery ui slider to resize the image.
Regards
What you can also do is make use of the setImage function of Jcrop, when the slider changes, call the setImage with the Jcrop api and set new width and height values like this:
var jcrop_api;
$('#cropbox').Jcrop({
onSelect: updateCoords
}, function() {
jcrop_api = this;
});
$("#imageslider").slider({
value: 100,
max: 100,
min: 1,
slide: function(event, ui) {
var value = $('#imageslider').slider('option', 'value');
var width = orgwidth * (value / 100);
var height = orgheight * (value / 100);
jcrop_api.setImage($(img).attr("src"), function() {
this.setOptions({
boxWidth: width,
boxHeight: height
});
});
$('#tester').val("org: "+orgwidth+" now: "+width);
}
});
What I am not sure about this technique is if it is the best solution because everytime you call the setImage function, jcrop creates a new Image object.
If what you want is that the resizing should be proportional, I don't think you need the slider (since it seems to be incompatible with jCrop). You could use jCrop and in the onChange event, ensure the proportionality (that is, implement the resizeImage function, modified).
That's what I think.
As an extension to Hermann Bier answer, i have added the jquery animation.
The resizing looks way better when it's animated :)
Implemented in Jcrop version: jquery.Jcrop.js v0.9.12
Locate the code:
ui: {
holder: $div,
selection: $sel
}
in jquery.Jcrop.js around line 1573
and replace it with:
ui: {
holder: $div,
selection: $sel
},
resizeImage: function(width, height) {
animationsTid = 500;
boundx = width;
boundy = height;
$($img2).animate({
width: width,
height: height,
}, { duration: animationsTid, queue: false });
$($img).animate({
width: width,
height: height,
}, { duration: animationsTid, queue: false });
$($div).animate({
width: width,
height: height,
}, { duration: animationsTid, queue: false });
$($trk).animate({
width: width,
height: height,
}, { duration: animationsTid, queue: false });
/*
//Old way of resizing, but without animation
$([$img2, $img, $div, $trk]).each(function(index, element){
element.width(width).height(height);
});
*/
}
Call to the function will animate the resize.
Feel free to delete the code between /* */ - I just kept it as an reference
Happy Coding :)

Categories

Resources