I saving polygon value inside angular $localStorage .
Once fabric js draw the object . my $localStorage changed.
var arr = [{
x: 81,
y: 58
}, {
x: 221,
y: 23
}, {
x: 247,
y: 158
}, {
x: 100,
y: 219
}, {
x: 81,
y: 58
}];
if(!$localStorage.mask)
$localStorage.mask = arr;
Is it a bug ?
Here the plunker
Indeed, FabricJS seems to modify the given points array for its internal purposes (i.e. offset), specifically in this piece of code (from https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.js):
...
//misko321: only for reference of what minX, minY, width and height are
_calcDimensions: function() {
var points = this.points,
minX = min(points, 'x'),
minY = min(points, 'y'),
maxX = max(points, 'x'),
maxY = max(points, 'y');
this.width = (maxX - minX) || 0;
this.height = (maxY - minY) || 0;
this.minX = minX || 0,
this.minY = minY || 0;
},
_applyPointOffset: function() {
// change points to offset polygon into a bounding box
// executed one time
this.points.forEach(function(p) {
p.x -= (this.minX + this.width / 2);
p.y -= (this.minY + this.height / 2);
}, this);
},
...
Together with the fact that ngStorage observes for any changes to the added objects, it updates those modified (internally by Fabric.js) coordinates.
The cleanest solution that comes to my mind at the moment is to copy the arr object (ref. Most elegant way to clone a JavaScript object) and send one of the objects to ngStorage and the other one to FabricJS.
Nevertheless, it would be nice, if ngStorage supported some sort of freeze() method, that would disable the AngularJS watch on this object, thus preventing it from further modifications.
Related
I'd like to be able to passively tween a property on my object, so that during the tween I can update this object and TweenLite will carry on.
For example, the following code will tween coordinates in an object from 0 to 15 over 15 seconds. Whilst this is happening, I also want to update the x and y variables of target.position, and I cannot do this as TweenLite seems to "hog" the object until it's done (as in, until 15 seconds have passed).
// target.position starts at { x:0, y: 0 }
TweenLite.to(target.position, 15, {
x: 15,
y: 15,
ease: Power4.easeOut
})
// for examples sake i'd like to do the following, yet it does not have an effect
setTimeout(function(){ target.position.x += 10 }, 1000)
setTimeout(function(){ target.position.y += 15 }, 2500)
setTimeout(function(){ target.position.x -= 17 }, 7500)
I solved my question by using the ModifiersPlugin that Tahir Ahmed recommended.
The ModifiersPlugin gives you two values in the callback, it's current value and the running total of the tween, I have named this cX and rT. What is returned in the callback will used by TweenLite in the next call, and given again as rT. This is handy so I can let ModifiersPlugin look after it's own running total, tween to x and y yet not actually update the target.position.. pretty useful.
All I do is work out the change needed, so the delta, which I call dX and add that to my target position, and passively tweening a variable is possible!
My code now looks something like this:
// just some dummy example data
target.position.x = 200
target.position.y = 300
x = 300
y = 400
TweenLite.to({ x: target.position.x, y: target.position.y }, 0.3, {
x: x,
y: y,
ease: Power4.easeOut,
modifiers: {
x: function(cX, rT) {
// get delta (current change in) value from running total - current
const dX = cX - rT.x
target.position.x += dX
// update running total with current delta
rT.x += dX
return rT.x
},
y: function(cY, rT) {
const dY = cY - rT.y
target.position.y += dY
rT.y += dY
return rT.y
}
}
})
Hitbox Overlay IIFE Code
//CSS Hitbox Solution 08-26-2015
//StackOverflow - https://stackoverflow.com/questions/32233084/show-an-element-without-hitbox-does-not-take-mouse-touch-input
//Detect MouseOver https://stackoverflow.com/questions/1273566/how-do-i-check-if-the-mouse-is-over-an-element-in-jquery
//Source: https://stackoverflow.com/questions/3942776/using-jquery-to-find-an-element-at-a-particular-position
//https://css-tricks.com/snippets/jquery/get-x-y-mouse-coordinates/
(function($) {
$.mlp = {
x: 0,
y: 0
}; // Mouse Last Position
function documentHandler() {
var $current = this === document ? $(this) : $(this).contents();
$current.mousemove(function(e) {
jQuery.mlp = {
x: e.pageX,
y: e.pageY
};
});
$current.find("iframe").load(documentHandler);
}
$(documentHandler);
$.fn.ismouseover = function(overThis) {
var result = false;
this.eq(0).each(function() {
var $current = $(this).is("iframe") ? $(this).contents().find("body") : $(this);
var offset = $current.offset();
result = offset.left <= $.mlp.x && offset.left + $current.outerWidth() > $.mlp.x && offset.top <= $.mlp.y && offset.top + $current.outerHeight() > $.mlp.y;
});
return result;
};
})(jQuery);
$('.notification-box').on("click", function() {
$("button").each(function(i) {
var iteratedButton = $('button:eq(' + i + ')');
var buttonID = iteratedButton.attr("id");
if (iteratedButton.ismouseover()) {
iteratedButton.toggleClass(buttonID);
}
});
});
Example 01: Overlay Example for context
Example 02: Concept for auto generating content - Derived from this stackoverflow question.
There is a way by which one can have multiple objects underneath an overlay that masks them. Then, there is a way to have the pointer interact with the elements underneath said overlay if the user clicks at the predetermined point. My question is, may someone please write the code that would, marry the concept of the <map> tag with the IIFE that detects if the point of reference the user clicked is that image and then, act as though it was clicked.
If that did not make sense, simply, I am looking for a process that deviates away from manually setting up coordinates for <area> or having to use tool (which are profound) such as http://www.image-maps.com/. Rather, we would let the pointer do all the work.
We have the following high utility + highly compatible methods: .offset(), .position(), elementFromPoint() and the ability to put elements behind a mask utilizing basic CSS.
So we could combine the IIFE Overlay hitbox method above + ???? = Profit (good bye mapping coordinates via <map>).
I just do not know what the ???? is. I do know that whatever the solution is, I would prefer that it works in all browsers (including IE 5).
Lastly, the process should be fairly automatic in design, setup and implementation.
Whoever creates it, please dub it autoMapperJs (as it would not be limited to images).
Update:
A core feature component of the ???? has been realized as noted by #Alex in the comments. CreateJs notices when the pointer is hovered over a non-transparent area of a image. That is powerful and should be standard in the tool created. It also seems to utilize .mousemove() and z-index. Please keep commenting, as collectively, I feel a solution can be found.
Here's a start. Put images into an array of layers and placements on canvas then run through them on mouse over for hit. Also put over images in layers array to draw that image when hit.
var can = document.getElementById('image-map');
var W = can.width;
var H = can.height;
var ctx = can.getContext('2d');
var layers = [];
var mouse = {x:0,y:0};
can.addEventListener('mousemove', function(evt) {
mouse = getMousePos(can, evt);
drawCanvas();
}, false);
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
main();
function main() {
initLayers();
drawCanvas();
}
function drawCanvas() {
ctx.clearRect(0, 0, W, H);
var hit = -1;
for (var i =layers.length; i--;) {
var c = layers[i];
if(maskHit(c.img, c.x, c.y)) {
hit = i;
break;
}
}
for (var i =0; i < layers.length; i++) {
var c = layers[i];
var img = hit === i ? c.hov : c.img;
ctx.drawImage(img, c.x, c.y);
}
ctx.drawImage(circ(10,"rgba(255,200,0,.75)"), mouse.x-10/2,mouse.y-10/2);
}
// UTILITY TO DRAW SAMPLE IMAGES
function circ(size, color) {
var can = document.createElement('canvas');
can.width = can.height = size;
var to_rad = Math.PI / 180;
var ctx = can.getContext('2d');
ctx.beginPath();
ctx.moveTo(size, size / 2);
ctx.arc(size / 2, size / 2, size / 2, 0, 360 * to_rad);
ctx.fillStyle = color;
ctx.fill();
return can;
}
function initLayers() {
var s = 75; // size
// PUT YOUR IMAGES IN A LAYERS ARRAY WITH X,Y COORDS FOR CANVAS
// PLACEMENT. X AND Y ARE TOP LEFT CORNDER OF IMAGE. STORE HOVER
// IMAGE FOR MOUSE HIT.
layers = [{
img: circ(s, "#090"),
hov: circ(s, "#C0C"),
x: 123,
y: 12
}, {
img: circ(s, "#F00"),
hov: circ(s, "#C0C"),
x: 63,
y: 12
}, {
img: circ(s, "#00F"),
hov: circ(s, "#C0C"),
x: 3,
y: 12
}];
}
var maskCan = document.createElement("canvas");
maskCan.width=maskCan.height=1;
var maskCtx = maskCan.getContext('2d');
function maskHit(img, x, y) {
// get relative coords to image upper left corner
x = mouse.x - x;
y = mouse.y - y;
if (x < 0 || y < 0 || x > img.width || y > img.height) return false;
//return 1; // square hit, no alpha check
// ALPHA CHECK - draw one pixel, get and check alpha.
// sx sy sw sh dx dy dw dh
maskCtx.clearRect(0,0,1,1);
maskCtx.drawImage(img, x, y, 1, 1, 0, 0, 1, 1);
var imageData = maskCtx.getImageData(0,0,1,1);
//console.log(imageData.data[3])
return imageData.data[3] === 255;
}
#image-map {
border: 1px solid #ACE;
}
<canvas id="image-map" width="200" height="100"></canvas>
Take a typical cubic bezier curve drawn in JavaScript (this example I googled...)
http://jsfiddle.net/atsanche/K38kM/
Specifically, these two lines:
context.moveTo(188, 130);
context.bezierCurveTo(170, 10, 350, 10, 388, 170);
We have a cubic bezier which starts at 188, 130, ends at 388, 170, and has controls points a:170, 10 and b:350, 10
My question is would it be possible to mathematically adjust the end point and control points to make another curve which is only a segment of the original curve?
The ideal result would be able to able to take a percentage slice of the bezier from the beginning, where 0.5 would draw only half of the bezier, 0.75 would draw most of the bezier (and so on)
I've already gotten working a few implementations of De Castelau which allow me to trace the contour of the bezier between [0...1], but this doesn't provide a way to mathematically recalculate the end and control points of the bezier to make a sub-bezier...
Thanks in advance
De Casteljau is indeed the algorithm to go. For a cubic Bezier curve defined by 4 control points P0, P1, P2 and P3, the control points of the sub-Bezier curve (0, u) are P0, Q0, R0 and S0 and the control points of the sub-Bezier curve (u, 1) are S0, R1, Q2 and P3, where
Q0 = (1-u)*P0 + u*P1
Q1 = (1-u)*P1 + u*P2
Q2 = (1-u)*P2 + u*P3
R0 = (1-u)*Q0 + u*Q1
R1 = (1-u)*Q1 + u*Q2
S0 = (1-u)*R0 + u*R1
Please note that if you want to "extract" a segment (u1, u2) from the original Bezier curve, you will have to apply De Casteljau twice. The first time will split the input Bezier curve C(t) into C1(t) and C2(t) at parameter u1 and the 2nd time you will have to split the curve C2(t) at an adjusted parameter u2* = (u2-u1)/(1-u1).
This is how to do it. You can get the left half or right half with this functin. This function is take thanks to mark from here: https://stackoverflow.com/a/23452618/1828637
I have it modified so it can be fit to a unit cell so we can use it for cubic-bezier in css transitions.
function splitCubicBezier(options) {
var z = options.z,
cz = z-1,
z2 = z*z,
cz2 = cz*cz,
z3 = z2*z,
cz3 = cz2*cz,
x = options.x,
y = options.y;
var left = [
x[0],
y[0],
z*x[1] - cz*x[0],
z*y[1] - cz*y[0],
z2*x[2] - 2*z*cz*x[1] + cz2*x[0],
z2*y[2] - 2*z*cz*y[1] + cz2*y[0],
z3*x[3] - 3*z2*cz*x[2] + 3*z*cz2*x[1] - cz3*x[0],
z3*y[3] - 3*z2*cz*y[2] + 3*z*cz2*y[1] - cz3*y[0]];
var right = [
z3*x[3] - 3*z2*cz*x[2] + 3*z*cz2*x[1] - cz3*x[0],
z3*y[3] - 3*z2*cz*y[2] + 3*z*cz2*y[1] - cz3*y[0],
z2*x[3] - 2*z*cz*x[2] + cz2*x[1],
z2*y[3] - 2*z*cz*y[2] + cz2*y[1],
z*x[3] - cz*x[2],
z*y[3] - cz*y[2],
x[3],
y[3]];
if (options.fitUnitSquare) {
return {
left: left.map(function(el, i) {
if (i % 2 == 0) {
//return el * (1 / left[6])
var Xmin = left[0];
var Xmax = left[6]; //should be 1
var Sx = 1 / (Xmax - Xmin);
return (el - Xmin) * Sx;
} else {
//return el * (1 / left[7])
var Ymin = left[1];
var Ymax = left[7]; //should be 1
var Sy = 1 / (Ymax - Ymin);
return (el - Ymin) * Sy;
}
}),
right: right.map(function(el, i) {
if (i % 2 == 0) {
//xval
var Xmin = right[0]; //should be 0
var Xmax = right[6];
var Sx = 1 / (Xmax - Xmin);
return (el - Xmin) * Sx;
} else {
//yval
var Ymin = right[1]; //should be 0
var Ymax = right[7];
var Sy = 1 / (Ymax - Ymin);
return (el - Ymin) * Sy;
}
})
}
} else {
return { left: left, right: right};
}
}
Thats the function and now to use it with your parameters.
var myBezier = {
xs: [188, 170, 350, 388],
ys: [130, 10, 10, 170]
};
var splitRes = splitCubicBezier({
z: .5, //percent
x: myBezier.xs,
y: myBezier.ys,
fitUnitSquare: false
});
This gives you
({
left: [188, 130, 179, 70, 219.5, 40, 267, 45],
right: [267, 45, 314.5, 50, 369, 90, 388, 170]
})
fiddle proving its half, i overlaid it over your original:
http://jsfiddle.net/K38kM/8/
Yes it is! Have a look at the bezier section here
http://en.m.wikipedia.org/wiki/De_Casteljau's_algorithm
It is not that difficult all in all.
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I am trying to make the following codes less ugly and not sure what to do. Are there any suggestions from you guys? Thanks
a lot.
if($element.is('builder') || $element.is('#options') ){
tooltipYPos=yPos + 35;
}
if($element.is('#button')){
tooltipXPos=xPos - 240;
}
if($element.is('#addn')){
tooltipXPos=xPos - 295;
tooltipYPos=yPos - 80;
}
if($element.is('#count')){
tooltipXPos=xPos + 180;
tooltipYPos=yPos - 90;
}
if($element.is('label')){
tooltipXPos=xPos + 80;
tooltipYPos=yPos - 90;
}
The general cure is to move the problem from code to data. One solution (demonstrated below) is to set up a JavaScript associative array keyed on the various ID tags and the values are value pairs X and Y offsets (in some cases one or the other is 0). At usage time, loop through the keys of the associative array, looking for matches. If so, add the X and Y offsets from the associative array onto toolTipXPos and toolTipYPos.
This will keep your offsets in one place, out of the way, and the code to manipulate them short and simple.
(untested, naturally)
// This can be stashed away anywhere.
var a = {
'#te_classbuilder': { X: 0, Y: 35 },
'#te_options': { X: 0, Y: 35 },
'#lesson-details-extend-button': { X: -240, Y: 0 },
'#asset-list-asset-add-button': { X: 295, Y: -80 },
'#asmnt_option_label_q_count': { X: 180, Y: -90 },
"label": { X: 80, Y: -90 }
}
// Put this where you need the actual evaluation to happen
jQuery.each(data, function(key, value) {
if ( $element.is(key) ) {
tooltipXPos = xPos + value.X;
tooltipYPos = yPos + value.Y;
}
});
Edit: changed to loop, so that label could be tested for, and not #label.
Another option would be to make use of jQuery's .data function to store the appropriate X and Y values on the elements themselves, like so:
$('label').data({ x: 80, y: -90 });
$('#te_classbuilder, #te_options').data({ x: 0, y: 35 });
$('#lesson-details-extend-button').data({ x: -240, y: 0 });
$('#asset-list-asset-add-button').data({ x: -295, y: -80 });
$('#asmnt_option_label_q_count').data({ x: 180, y: -90 });
Then, when it comes time to modify your tooltip position values, no conditional statements are required. Simply retrieve the x and y data attributes from $element.
tooltipXPos = xPos + $element.data('x');
tooltipYPos = yPos + $element.data('y');
This, of course, assumes that any element that may be assigned to $element will have previously had .data called on it with appropriate x and y values.
Can you work out the height/width of $element and then alter tooltipXPos/tooltipYPos accordingly, instead of hard-coding the deltas?
In first if you can combine conditions:
if ($element.is('#te_classbuilder, #te_options')) {
tooltipYPos = yPos + 35;
}
If we could not make some big changes then I would write it this way:
if ($element.is('#te_classbuilder, #te_options')) tooltip = { x: 0, y: yPos + 35 };
if ($element.is('#lesson-details-extend-button')) tooltip = { x: xPos - 240, y: 0 };
if ($element.is('#asset-list-asset-add-button')) tooltip = { x: xPos - 295, y: yPos - 80 };
if ($element.is('#asmnt_option_label_q_count')) tooltip = { x: xPos + 180, y: yPos - 90 };
if ($element.is('label')) tooltip = { x: xPos + 80; y: yPos - 90 };
I'm trying to do something I thought would be rather simple. I've an object that I move around stepwise, i.e. I receive messages every say 100 milliseconds that tell me "your object has moved x pixels to the right and y pixels down". The code below simulates that by moving that object on a circle, but note that it is not known in advance where the object will be heading in the next step.
Anyway, that is pretty simple. But now I want to also tell the object, which is actually a set of subobjects, that it is being rotated.
Unfortunately, I am having trouble getting Raphaël to do what I want. I believe the reason is that while I can animate both translation and rotation independently, I have to set the center of the rotation when it starts. Obviously the center of the rotation changes as the object is moving.
Here's the code I'm using and you can view a live demo here. As you can see, the square rotates as expected, but the arrow rotates incorrectly.
// c&p this into http://raphaeljs.com/playground.html
var WORLD_SIZE = 400,
rect = paper.rect(WORLD_SIZE / 2 - 20, 0, 40, 40, 5).attr({ fill: 'red' }),
pointer = paper.path("M 200 20 L 200 50"),
debug = paper.text(25, 10, ""),
obj = paper.set();
obj.push(rect, pointer);
var t = 0,
step = 0.05;
setInterval(function () {
var deg = Math.round(Raphael.deg(t));
t += step;
debug.attr({ text: deg + '°' });
var dx = ((WORLD_SIZE - 40) / 2) * (Math.sin(t - step) - Math.sin(t)),
dy = ((WORLD_SIZE - 40) / 2) * (Math.cos(t - step) - Math.cos(t));
obj.animate({
translation: dx + ' ' + dy,
rotation: -deg
}, 100);
}, 100);
Any help is appreciated!
If you want do a translation and a rotation too, the raphael obj should be like that
obj.animate({
transform: "t" + [dx , dy] + "r" + (-deg)
}, 100);
Check out http://raphaeljs.com/animation.html
Look at the second animation from the top on the right.
Hope this helps!
Here's the code:
(function () {
var path1 = "M170,90c0-20 40,20 40,0c0-20 -40,20 -40,0z",
path2 = "M270,90c0-20 40,20 40,0c0-20 -40,20 -40,0z";
var t = r.path(path1).attr(dashed);
r.path(path2).attr({fill: "none", stroke: "#666", "stroke-dasharray": "- ", rotation: 90});
var el = r.path(path1).attr({fill: "none", stroke: "#fff", "stroke-width": 2}),
elattrs = [{translation: "100 0", rotation: 90}, {translation: "-100 0", rotation: 0}],
now = 0;
r.arrow(240, 90).node.onclick = function () {
el.animate(elattrs[now++], 1000);
if (now == 2) {
now = 0;
}
}; })();