KineticJS - update drawFunc after an object on another stage has been moved - drag

With the help of a fellow stackoverflow user, I am able to change the position of a two lines and a circle on the stage using the following:
var circle2 = new Kinetic.Circle({
drawFunc: function(canvas) {
var context2 = canvas.getContext();
var centerX2 = blueLine2.getPosition().x;
centerY2 = greenLine2.getPosition().y;
context2.drawImage(gArrow2, -156, -23 + centerY2, 11, 23);
context2.drawImage(gArrow2, 156, -23 + centerY2, 11, 23);
context2.drawImage(bArrow2, centerX2, 156, 23, 11);
context2.drawImage(bArrow2, centerX2, -156, 23, 11);
context2.beginPath();
context2.arc(centerX2, centerY2, this.getRadius(), 0, 2 * Math.PI, false);
context2.lineWidth = this.getStrokeWidth();
context2.strokeStyle = this.getStroke();
context2.stroke();
},
x: cx + gx,
y: cy + gy,
radius: 70,
stroke: '#00ffff',
strokeWidth: 3,
opacity: 0.5
});
layer2.add(circle2);
It works great, now my challenge is if I move a line on a second stage for example the horizontal line, I can also move the horizonal line on the first stage using:
greenLine2.on('dragend', function (event) {
var y1 = greenLine2.getPosition().y;
greenLine3.setPoints([0, 256 + y1, 512, 256 + y1]);
centerY3 = 256 + y1;
layer3.draw();
layer2.draw();
});
However I can't update the layer to move also the vertical line and the circle. I would appreciate your suggestions, thanks in advance.

Lets say that greenLine2 is the one you're moving, and you want greenLine3 to move to the same position on the other stage. I'm going to assume the stages are the same size, but you can change up the code to account for these changes.
greenLine2.on('dragmove', function (event) {
var userPos = stage.getUserPosition(); //if this doesnt work the way you need, try a different approach, such as below:
//var userPos = greenLine.getPosition(); //other possibility
greenLine3.setPosition(userPos);
layer3.draw();
layer2.draw();
});
and if you want other things to move as well, you can do the same kind of code using .setPosition() with some offset so that the drawing is relative.
Another approach would be to create a group in each stage, and make the group draggable, that way, you can drag all the items in a group at the same time, and synchronously across stages.

Related

How to download entire react component as pdf?

I have a resume component that I want to download as a pdf. I'm able to do that using html2canvas and jspdf.
const printDocument = () => {
const input = document.getElementById('resume-wrapper');
html2canvas(input)
.then((canvas) => {
const imgData = canvas.toDataURL('image/png');
console.log("clicked",typeof(imgData))
const pdf = new jsPDF();
pdf.addImage(imgData, 'JPEG', 10, 10);
pdf.save("download.pdf");
})
};
But, I'm getting a small part of my resume component instead of the entire component. The pdf is smaller width and height wise. What can I do to get the entire component?
Your problem is basic page control (PDF is a page XY language) so your new page needs to be defined well and the image needs to work XY within that.
Here is a small sample to show how the two (page and image) inter-relate.
Note the image is FORCED to the defined area, so units need to be calculated before application. If placement is not defined well you will cause stretching (here its desired for image 1 but not image 2) or in your case truncation.
// You'll need to make your image into a Data URL
// From https://contemporarycodes.com/dataUrl/Generator.en.html (minimal size)
var imgData1 = 'data:image/jpeg;base64,/9j/...too big for SO example thus use demodata...';
var imgData2 = 'data:image/png;base64,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wbOKNAGiMvXyOsKUCZBeE7lpthEPpSi5GwTP6lwJEJWpl9cRpiSX5OVySOO7abYRjySEUETuBsGzAPnPkgSIytTL6whTkkvycjmk8d0024hHEkIoIneD4FmAnABRSUrdjx+EKckliSY1A26abcRTq6BVkLtB8KyhzjcQlafX1xGmJJfk9YIIBLhpthGPIINUQu4GwbMEOj9hqTSljjAluSRRpGbATbONeGoVtApyNwieNdT5BqLy9Po6wpTkkrxeEIEAN8024hFkkErI3SB4lkDnG4hKU+oIU5JLEkVqBtw024inVkGrIHeD4FlDnW8gKk+vryNMSS7J6wURCHDTbCMeQQaphNwNgmcJdL6BqDSljjAluSRRpGbATbONeGoVtApyNwieNdT5BqLy9Po6wpTkkrxeEIEAN8024hFkkErI3SB4lkDnG4hKU+oIU5JLEkVqBtw024inVkGrIHeD4FlDnW8gKk+vryNMSS7J6wURCHDTbCMeQQaphNwNgmcJdL6BqDSljjAluSRRpGbATbONeGoVtApyNwieNdT5BqLy9Po6wpTkkrxeEIEAN8024hFkkErI3SB4lkDnG4hKU+oIU5JLEkVqBtw024inVkGrIHeD4FlDnW8gKk+vryNMSS7J6wURCHDTbCMeQQaphNwNgmcJdL6BqDSljjAluSRRpGbATbONeGoVtApyNwieNdSH30DUpqkLA2EgDISB/Qwc/YNS++nIhGEgDISBMKAykABRmUpdGAgDYSAM/D8GEiAxRBgIA2EgDHzEQALkI9ryoTAQBsJAGEiAxANhIAyEgTDwEQMJkI9oy4fCQBgIA2EgARIPhIEwEAbCwEcMJEA+oi0fCgNhIAyEgf8As1aFE0AtmmIAAAAASUVORK5CYII='
// Set up page structure = landscape and metric units plus dimensions
var doc = new jsPDF("l", "mm", [297,210]);
// Build page 1
doc.setPage(1);
doc.setFont('helvetica', 'italic');
doc.setFontSize(30); //Font is Points not page units = approx. 10 mm total but 2 mm below baseline so set 8 mm down !
doc.text('I am a top left heading on page 1', 8.5, 8);
doc.addImage(imgData1, "JPG", 8.5, 10, 280, 190, "A Limousine Cat", "none", 0);
doc.addImage(imgData2, "PNG", 238.5, 150, 50, 50, "Hello World!", "none", 0);
doc.setFillColor(255, 255, 127); //Prepare colour of rectangle
doc.rect(104.5, 114.5, 80, 10, 'F');
doc.text('I am text overlay', 105, 123); //Keeps previous font height and style
doc.setFont('helvetica', 'normal');
doc.setFontSize(30); //Font is Points not page units = approx. 10 mm total but 2 mm below baseline so set 8 mm down !
doc.text('I am a bottom right footing on page 1', 124, 207.5);
doc.save("download.pdf");

Loop in the svg.js

rect
.animate(1000,"<>",0).dmove(50,0)
.animate(1000,"<>",0).dmove(-10,0)
.animate(1000,"<>",0).dmove(20,0)
.animate(1000,"<>",0).dmove(-60,0).loop(true, true);
Why (and should it?) does the loop not repeat the entire animation? He skips 2 and the 3 step.
Demonstration: https://codepen.io/Andreslav/pen/BxGygp
A very similar issue was solved here. The solution can be adapted for your problem:
let w = 100,
h = 100,
t = 1000,
draw = SVG('svg').size('100%', '100%').viewbox(0,0,w,h);
function animation() {
draw.rect(10,10)
.animate(t,">",0).dx(w/2).width(20).height(10)
.animate(t,">",0).dy(h/2).width(15).height(15)
.animate(t,">",0).dx(-w/2).width(10).height(15)
.animate(t,">",0).dy(-h/2).width(10).height(10).after(animation)
}
animation()
It appears the loop only works on the last action.
You can try a different approach to accomplish the same thing by using a path.
E.g: (you'll need to adjust to replicate the same points)
let width = 1000,
height = 100,
draw = SVG('svg').size('100%', '100%').viewbox(0,0,width,height);
// use path to replicate movement
const path = draw.path("M200 0 H 250 H 100 H 120 H 80")
const length = path.length()
path.fill('none').stroke({width:1, color: '#ccc'})
const rect = draw.rect(100, 100)
rect.animate(5000, '<>').during(function(pos, morph, eased){
var p = path.pointAt(eased * length)
rect.center(p.x, p.y)
}).loop(true, true)
(from tutorials)

Accessing the number of elements in an array and applying gravity behaviour

I'm having issues with getting ALL elements of an array to fall using the Gravity module. I have managed to get the LAST element in the array to fall and then the remaining elements just stay at the top of the screen during testing. Upon debugging
I am using UIKit and want to understand this language thoroughly before using other various engines such as SpriteKit and GameplayKit.
func mainGame()
{
let cars = ["car5", "car1", "car6", "car3", "car2", "car4"]
var random2 = Int(arc4random_uniform(UInt32(cars.count))) + 1
for i in 1...random2
{
let image = UIImage(named: cars[i - 1])
let carView = UIImageView(image: image!)
carView.frame = CGRect(x:i * 52, y:0 , width: 40, height: 50)
view.addSubview(carView)
dynamicAnimator = UIDynamicAnimator(referenceView: self.view)
gravityBehavior = UIDynamicItemBehavior(items: [carView]) //cars falling
dynamicAnimator.addBehavior(gravityBehavior)
collisionBehavior = UICollisionBehavior(items: [carView, mainCar]) //collide
collisionBehavior.translatesReferenceBoundsIntoBoundary = false
gravityBehavior.addLinearVelocity(CGPoint(x: 0, y: 200), for: carView)
dynamicAnimator.addBehavior(collisionBehavior)
}
collisionBehavior.addBoundary(withIdentifier: "Barrier" as NSCopying, for: UIBezierPath(rect: mainCar.frame))
collisionBehavior.removeAllBoundaries()
}
With the game so far the last car in the array falls and the main player car that I control has collision behaviour, which is a big step for me!
You are creating a new UIDynamicAnimator with each iteration of the loop and assigning it to dynamicAnimator. That is why only the last element is working, because it is the last one assigned to that variable.
To fix it, just move this line to somewhere that would only be called once.
dynamicAnimator = UIDynamicAnimator(referenceView: self.view)
viewDidLoad is a possible place that should work.
UIKitDynamics is backwards of most similar frameworks. You don't animate the object. You have an animator and attach objects to it. As Clever Error notes, you only want one animator in this case.
The key point is that you don't attach gravity to cars; you attach cars to behaviors (gravity), and then behaviors to the animator. Yes, that's bizarre and backwards.
I haven't tested this, but the correct code would be closer to this:
func mainGame()
{
let cars = ["car5", "car1", "car6", "car3", "car2", "car4"]
var random2 = Int(arc4random_uniform(UInt32(cars.count))) + 1
var carViews: [UIImageView] = []
dynamicAnimator = UIDynamicAnimator(referenceView: self.view)
// First create all the views
for i in 1...random2
{
let image = UIImage(named: cars[i - 1])
let carView = UIImageView(image: image!)
carView.frame = CGRect(x:i * 52, y:0 , width: 40, height: 50)
view.addSubview(carView)
carViews.append(carView)
}
// and then attach those to behaviors:
gravityBehavior = UIGravityBehavior(items: carViews) //cars falling
dynamicAnimator.addBehavior(gravityBehavior)
collisionBehavior = UICollisionBehavior(items: carView + mainCar) //collide
collisionBehavior.translatesReferenceBoundsIntoBoundary = false
dynamicAnimator.addBehavior(collisionBehavior)
collisionBehavior.addBoundary(withIdentifier: "Barrier" as NSCopying, for: UIBezierPath(rect: mainCar.frame))
collisionBehavior.removeAllBoundaries()
// You don't need this; it's built into Gravity
// gravityBehavior.addLinearVelocity(CGPoint(x: 0, y: 200), for: carView)
}
The main way that UIKitDynamics is different than most animation frameworks is that things that are animated don't know they're being animated. You can't ask a car what behaviors it has, because it doesn't have any. A UIDynamicAnimator basically is a timing loop that updates the center and transform of its targets. There's really not anything fancy about it (in contrast to something like Core Animation which has many fancy things going on). With a little iOS experience, you could probably implement all of UIKitDynamics by hand with a single GCD queue (it probably doesn't even need that, since it runs everything on main....)

d3.js adding circle focus points to multi-line series

After 6 long hours, I managed to add just a couple of more lines to my example, following my previous post (D3 tooltip show values from nested dataset) concerning the use of tooltips.
Now I am stuck at a different point - I can't make circle points that snap to the line points. Other users have already pointed me to a few directions (thanks #Mark), but still haven't been able to combine everything and make it work as I want it.
I have created one circle for each line with its corresponding line color. When hovering over with the mouse, a tooltip with all the lines' values appears and the circles must be positioned on the lines on the x and y axis.
My problem lies in the following piece of code, located inside the mousemove function, in line #106 of this fiddle edit: updated fiddle (https://jsfiddle.net/2en21Lqh/13/):
d3.selectAll(parent + ' .d3-focuspoint')
.classed("hidden", false)
.attr("cx", xz(lastDate))
.attr("cy", function(c) {
return d3.map(c.values, function(d,i) {
if (lastDate == d.date) {
console.log(d.value);
return d.value;
}
})
});
The circles are already bound to the existing data (two days ago I wouldn't have figured this on my own! - at least I am sligthly improving! :/ ), so I am trying to apply the correct cy value but can't figure out the way to do it. The function inside cy returns an object rather than the d.value I am looking for. What am I doing wrong? I've been trying for hours to find any similar examples but can't find any :/
edit: even pointers to the right direction would help :/
Try this:
var mousemoveFunc = function(d, i) {
var x0 = xz.invert(d3.mouse(this)[0]);
var lastDate,
cys = [], //<-- create array to hold y values for circles
ds = []; //<-- create array to hold tooltip text
dataGroup.forEach(function(e) { //<-- loop the data (I removed the map since we now need to return two things)
var i = bisectDate(e.values, x0, 1),
d0 = e.values[i - 1],
d1 = e.values[i];
var d = x0 - d0.date > d1.date - x0 ? d1 : d0;
lastDate = d.date; //<-- hold onto the date (same for all xs)
cys.push(d.value); //<-- hold onto the y value for all circles
ds.push(e.key + " " + d.value); //<-- make the tooltip line
});
var mouse = d3.mouse(svg.node()).map(function(d) {
return parseInt(d);
});
var left = Math.min(containerwidth, mouse[0]+margin.left+margin.right),
top = Math.min(containerheight, mouse[1]+margin.top+margin.right);
d3.selectAll(parent + ' .d3-focuspoint')
.classed("hidden", false)
.attr("cx", xz(lastDate)) //<-- set x position
.attr("cy", function(d,i) {
return yz(cys[i]); //<-- loop the 3 circles and set y position
});
tooltip
.html(lastDate.toString() + "<br/>" + ds.join("<br/>"))
.classed('hidden', false)
.style('left', left + 'px')
.style('top', top + 'px');
};
Updated fiddle.

Swift - Create circle and add to array

I've been looking for a little while on how to create a circle in Swift, but it seems quite complicated. I want to create a circle at run time, with a specified x and y, and width and height. I then want to add this circle to an array where I can create more circles and add to it.
How do I do this?
Edit: This is what I've tried so far:
var center : CGPoint = touchLocation
var myContext : CGContextRef = UIGraphicsGetCurrentContext()
let color : [CGFloat] = [0, 0, 1, 0.5]
CGContextSetStrokeColor (myContext, color);
CGContextStrokeEllipseInRect (myContext, CGRectMake(touchLocation.x, touchLocation.y, 20, 20));
touchLocation is the location of the users finger. This crashes on execution on this line:
var myContext : CGContextRef = UIGraphicsGetCurrentContext()
The error says "Unexpectedly found nil while unwrapping an Optional value
Also, this doesn't allow me to add the circle to an array, because I don't know what variable type it is.
There are many ways to draw a circle, here is a snippet that I have been hacking with:
func circleWithCenter(c:CGPoint, radius r:CGFloat,
strokeColor sc: UIColor = UIColor.blackColor(),
fillColor fc: UIColor = UIColor.clearColor()) -> CAShapeLayer {
var circle = CAShapeLayer()
circle.frame = CGRect(center:c, size:CGSize(width:2*r, height:2*r))
circle.path = UIBezierPath(ovalInRect:circle.bounds).CGPath
circle.fillColor = fc.CGColor
circle.strokeColor = sc.CGColor
circle.fillColor = fc == UIColor.clearColor() ? nil : fc.CGColor
return circle
}
Note that I extended CGRect (using Swift specific features) to add a initializer that takes a center, but that is not material to your question.
Your code is not "creating" a circle as an object, it is attempting to draw one in the graphics context. What you need to do is to create a bezier path object, draw into that and save the path in your array. Something like:
var circleArray = [CGMutablePathRef]()
// ...
let path: CGMutablePathRef = CGPathCreateMutable()
CGPathAddArc(path, nil, touchLocation.x, touchLocation.y, radius, 0, M_PI * 2, true)
circleArray += [path]
You then need to stroke the path(s) in your draw routine.

Resources