http://40.117.122.212/
I want to add more images dynamically on scrolling the canvas, i don't wanna see any empty areas on the canvas.
Any ideas on how to get that done ?
I use paperjs, I create about 90 images and they are moving on the canvas randomly.
Each time you scroll, you can check if there is space for more images at the bottom of the screen (and maybe at the top too for a better effect?) and if this is true, add more images.
In order to get that working, you will certainly need to find a way to quickly get the topest/lowest items to check how far they are from view bounds.
To better understand the concept, I reduced the problem to a simple case involving only one column of circles.
Here is the sketch demonstrating the solution.
Code should be self-explainatory and should allow you to transpose it to your specific case.
// Set gris Size.
const gridSize = 100;
// Calculate and store several useful metrics.
const viewHeight = view.bounds.height;
const itemsCount = Math.floor(viewHeight / gridSize);
// For the simplicity of the demo, items are only drawn in one column so x
// coordinate is constant.
const x = view.center.x;
// Create original items to fill the screen.
let items = [];
for (let i = 0; i < itemsCount; i++) {
items.push(createItem(i * gridSize));
}
// Center them on the screen.
project.activeLayer.position = view.center;
// On scroll...
view.element.onmousewheel = function(event) {
// ...translate layer up or down.
// Using layer translation instead of view scroll cause items coordinates
// to be updated which will help us in later calculations.
const translation = event.deltaY > 0 ? new Point(0, 10) : new Point(0, -10);
project.activeLayer.translate(translation);
// Trigger items addition process.
addItemsIfNeeded();
};
// Simply create a random colored, horizontally centered circle, at given
// y position.
function createItem(y) {
return new Path.Circle({
center: new Point(x, y),
radius: gridSize / 4,
fillColor: Color.random()
});
}
// Check for empty space at the bottom or the top of the page and create as
// many items as needed to fill the empty space.
function addItemsIfNeeded() {
// Get extremas items y positions.
const lastItemY = items[items.length - 1].position.y;
const firstItemY = items[0].position.y;
const deltaBottom = viewHeight - lastItemY;
// If there is empty space at bottom...
if (deltaBottom > gridSize) {
// ...add items at bottom.
const itemsNeededCount = Math.floor(deltaBottom / gridSize);
addItems(itemsNeededCount, lastItemY);
// If there is empty space at top...
} else if (firstItemY > gridSize) {
// ...add items at top.
const itemsNeededCount = Math.floor(firstItemY / gridSize);
addItems(itemsNeededCount, firstItemY, true);
}
}
// Create given number of items and add them to the begining or the end of the
// stack.
function addItems(count, referenceItemY, before) {
// For each new item...
for (let i = 1; i <= count; i++) {
// ...calculate y position...
const y = before
? referenceItemY - i * gridSize
: referenceItemY + i * gridSize;
// ...create item...
const item = createItem(y);
// ...add it to the stack.
if (before) {
items.unshift(item);
} else {
items.push(item);
}
}
}
Related
Found a strange behaviour that when I keep adding or toggle display sprite object. That makes my game screen shakes when toggle some of optional UIs .
Tried to reproduce this issue with simplified codes. Here is the experiment.
The red line shows the original position of first square.png
When I keep adding sprites on the same position, appears that pixels moved, the square not stack up in different positions
When hiding sprite except the first square, seems it back to correct position
var MyScene = cc.Scene.extend({
onEnterTransitionDidFinish: function(){
this._super();
this.initComponents();
},
initComponents: function () {
//create additional square container
var node = new cc.Node();
this.node = node;
node.setPosition(90, 90);
this.attach(node);
this.addChild(node);
//draw first square
var sprite = new cc.Sprite("square.png");
sprite.setPosition(50,50);
this.addChild(sprite);
//listen to keyboard, add square / toggle
if ('keyboard' in cc.sys.capabilities) {
cc.eventManager.addListener({
event: cc.EventListener.KEYBOARD,
onKeyPressed: function (keyCode, event) {
switch (keyCode) {
//toggle additional squares
case cc.KEY.q:
this.node.setVisible(!this.node.isVisible());
break;
//attach more squares
case cc.KEY.w:
this.attach(node);
break;
}
}.bind(this)
}, this);
}
//draw measure line
var line = new cc.DrawNode();
line.drawRect(cc.p(130,0), cc.p(132,150),cc.color(255,0,0,255),0);
this.addChild(line);
},
/**
* Attach more squares
* #param node
*/
attach: function (node) {
var xp = 0;
var yp = 0;
for (var i = 0; i < 5; i++) {
var sp = new cc.Sprite("square.png");
node.addChild(sp);
sp.setPosition(xp * 180, yp * 180);
xp++;
if (xp > 15) {
xp = 0;
yp++;
}
}
}
});
It's difficult to answer you without the context, what API do you use ? What is cc ?
Anyway, by comparing the screenshots you provided it seems that your squares effectively moved away. The strange corners appears because you don't clear the screen between each frame.
This produce a trail, it's not the pixels that moved, but the squares themselves.
Try those steps :
print their positions each time you add a new sprite
fix the length of view
try to print a square instead of using an image
Edit your question if you have more information, and then I will edit this answer according to your additional information.
Found that the issue can be improved / resolve by assign everything into negative z order
var children = this.getChildren();
var avicinarAka = -99999;
for (var k in children) {
if (children[k]) {
children[k].setLocalZOrder(avicinarAka++);
}
}
Does someone know how is this animation build, which js framework is used or something that can help me get to know to recreate something similar?
Banner BG animation ---> https://envylabs.com/
Thanks in advance!
I can't tell with what library this animation was build with, because it is hidden in React bundled code, but I can show you a way to do something similar with Paper.js.
By looking at the animation, it seems that rules are:
a circle is influenced by mouse pointer if it is under a certain distance from it
the closest to the mouse pointer a circle is:
the bigger it becomes
the farther from the window center it goes.
Here is a Sketch implementing this.
//
// CONSTANTS
//
// user defined
var ROWS_COUNT = 10; // number of rows in the grid
var COLUMNS_COUNT = 10; // number of columns in the grid
var MOUSE_INFLUENCE_RADIUS = 350; // maximal distance from mouse pointer to be influenced
var INFLUENCE_SCALE_FACTOR = 1; // maximal influence on point scale
var INFLUENCE_POSITION_FACTOR = 15; // maximal influence on point position
// computed
var STEP_X = view.bounds.width / COLUMNS_COUNT;
var STEP_Y = view.bounds.height / ROWS_COUNT;
var RADIUS = Math.min(STEP_X, STEP_Y) * 0.1;
//
// ITEMS
//
// create a circle for each points in the grid
var circles = [];
for (var i = 0; i < COLUMNS_COUNT; i++)
{
for (var j = 0; j < COLUMNS_COUNT; j++)
{
var gridPoint = new Point((i + 0.5) * STEP_X, (j + 0.5) * STEP_Y);
circles.push(new Path.Circle({
center : gridPoint,
radius : RADIUS,
fillColor : 'black',
// matrix application is disabled in order to be able to manipulate scaling property
applyMatrix: false,
// store original center point as item custom data property
data : {gridPoint: gridPoint}
}));
}
}
//
// EVENTS
//
function onMouseMove(event)
{
for (var i = 0; i < circles.length; i++)
{
var circle = circles[ i ];
var gridPoint = circle.data.gridPoint;
var distance = event.point.getDistance(gridPoint);
// only influence circles that are in mouse influence zone
if (distance <= MOUSE_INFLUENCE_RADIUS)
{
var influence = 1 - distance / MOUSE_INFLUENCE_RADIUS;
// the closest the circle is from the mouse pointer
// the bigger it is
circle.scaling = 1 + influence * INFLUENCE_SCALE_FACTOR;
// the farthest it is from view center
circle.position = gridPoint + (gridPoint - view.center).normalize(influence * INFLUENCE_POSITION_FACTOR);
}
else
{
// reset circle state
circle.scaling = 1;
circle.position = gridPoint;
}
}
}
Your question is too open.
But there are some libraries that will save you a lot of time:
D3.js
Processing.js
Paper.js
There are a lot more, but it depends on what you need.
I'm trying to render an arrow type shape in Paper.js. I have been able to create the segments that render out the tip of the arrow, but have been unable to create any further points that would finish the outline of the arrow. For my own testing purposes, it's currently only 3 lines, however I need to create a shape that can have fills, etc, so I need to be able to outline the arrow and have the group move dynamically when the mouse is dragged in a direction. I need a fat arrow!
Every point I choose, despite being relative to the position of the current vector, seem to rotate on their own when the arrow is manipulated.
Been hitting my head against this for days with no luck.
Here's what I'm working with -
var vectorStart, vector;
var vectorItem = new Group();
onMouseDrag = function (event) {
var arrowLength = 50;
vectorItem.remove();
engaged = true;
vectorStart = view.center;
var end = vectorStart + vector;
vector = event.point - vectorStart;
console.log('arrow pointer location: ' + event.point);
var vectorArrow = vector.normalize(arrowLength);
vectorItem = new Group([
new Path([vectorStart, end]),
new Path([
end + vectorArrow.rotate(120),
end,
end + vectorArrow.rotate(-120),
]),
]);
vectorItem.strokeWidth = 1;
vectorItem.strokeColor = 'black';
this.onMouseUp = function() {
vectorItem.remove();
}
}
Here's a link to a Sketch containing my code.
What I'm not understanding is how to add points to the Path that generates the arrow in order to create a shape. Everything seems to rotate on it's own and doesn't behave in the way that I need it to.
Any help would be great!
A simple way of drawing an arrow outline is by combining 3 rectangles.
Paper.js allow you to do this with Path.unite() method.
Here is an overview of the drawing algorithm
Here is a Sketch demonstrating my solution.
//
// CONSTANTS
//
// user defined
var STROKE_WIDTH = 40;
var HEAD_LENGTH = 300;
var STYLE = {
fillColor : 'orange',
strokeColor: 'black',
strokeWidth: 5
};
// computed
var WIDTH = STROKE_WIDTH * 2;
var DIAGONAL = Math.sqrt(Math.pow(STROKE_WIDTH * 2, 2) * 2);
//
// METHODS
//
/**
* Draws an arrow between two points.
* For simplicity sake, arrow is drawn horizontally at origin first
* then it is moved and rotated according to start / end points.
* It is composed of 3 rectangles which are united into a single shape.
* #param {Point} start
* #param {Point} end
*/
function drawArrow(start, end)
{
// calculate distance between points
var distance = start.getDistance(end);
// make sure it is not lower than diagonal
if (distance < DIAGONAL)
{
distance = DIAGONAL;
}
// draw rectangles
var directionRectangle = new Path.Rectangle(new Point(0, -STROKE_WIDTH), new Point(distance - DIAGONAL, STROKE_WIDTH));
var topRectangle = new Path.Rectangle(new Point(0, -STROKE_WIDTH), new Point(HEAD_LENGTH, STROKE_WIDTH));
// move top rectangle to the right
topRectangle.translate(directionRectangle.bounds.rightCenter - topRectangle.bounds.rightCenter + [ WIDTH, 0 ]);
// make bottom rectangle by cloning top one
var bottomRectangle = topRectangle.clone();
// offset top and bottom rectangles
topRectangle.position -= [ 0, STROKE_WIDTH ];
bottomRectangle.position += [ 0, STROKE_WIDTH ];
// rotate then to form arrow head
topRectangle.rotate(45, topRectangle.bounds.bottomRight - [ WIDTH, 0 ]);
bottomRectangle.rotate(-45, bottomRectangle.bounds.topRight - [ WIDTH, 0 ]);
// join the 3 rectangles into one path
var arrow = directionRectangle.unite(topRectangle).unite(bottomRectangle);
// move and rotate this path to fit start / end positions
arrow.translate(start - directionRectangle.bounds.leftCenter);
arrow.rotate((end - start).angle, start);
// apply custom styling
arrow.style = STYLE;
// remove construction items
directionRectangle.remove();
topRectangle.remove();
bottomRectangle.remove();
}
function onMouseDrag(event)
{
// clear canvas
project.clear();
// draw arrow according to mouse position
drawArrow(event.downPoint, event.point);
}
//
// INIT
//
// display instructions
new PointText({
point : view.center,
justification: 'center',
content : 'Draw arrow by dragging and dropping with your mouse.'
});
Here's some code that creates an arrow. The object is initialized with mouse down point and draws an arrow with the tip at the point the mouse is dragged to.
function Arrow (mouseDownPoint) {
this.start = mouseDownPoint;
this.headLength = 20;
this.tailLength = 9;
this.headAngle = 35;
this.tailAngle = 110
}
Arrow.prototype.draw = function (point) {
var end = point;
var arrowVec = this.start.subtract(end);
// parameterize {headLength: 20, tailLength: 6, headAngle: 35, tailAngle: 110}
// construct the arrow
var arrowHead = arrowVec.normalize(this.headLength);
var arrowTail = arrowHead.normalize(this.tailLength);
var p3 = end; // arrow point
var p2 = end.add(arrowHead.rotate(-this.headAngle)); // leading arrow edge angle
var p4 = end.add(arrowHead.rotate(this.headAngle)); // ditto, other side
var p1 = p2.add(arrowTail.rotate(this.tailAngle)); // trailing arrow edge angle
var p5 = p4.add(arrowTail.rotate(-this.tailAngle)); // ditto
// specify all but the last segment, closed does that
this.path = new paper.Path(this.start, p1, p2, p3, p4, p5);
this.path.closed = true;
this.path.strokeWidth = 1
this.path.strokColor = 'black'
this.path.fillColor = 'black'
return this.path
}
I like the tapered tail but you can get rid of that by fiddling with the constructor lengths.
Here's a sketch with the mouse handling
I've created a pinch filter/effect on canvas using the following algorithm:
// iterate pixels
for (var i = 0; i < originalPixels.data.length; i+= 4) {
// calculate a pixel's position, distance, and angle
var pixel = new Pixel(affectedPixels, i, origin);
// check if the pixel is in the effect area
if (pixel.dist < effectRadius) {
// initial method (flawed)
// iterate original pixels and calculate the new position of the current pixel in the affected pixels
if (method.value == "org2aff") {
var targetDist = ( pixel.dist - (1 - pixel.dist / effectRadius) * (effectStrength * effectRadius) ).clamp(0, effectRadius);
var targetPos = calcPos(origin, pixel.angle, targetDist);
setPixel(affectedPixels, targetPos.x, targetPos.y, getPixel(originalPixels, pixel.pos.x, pixel.pos.y));
} else {
// alternative method (better)
// iterate affected pixels and calculate the original position of the current pixel in the original pixels
var originalDist = (pixel.dist + (effectStrength * effectRadius)) / (1 + effectStrength);
var originalPos = calcPos(origin, pixel.angle, originalDist);
setPixel(affectedPixels, pixel.pos.x, pixel.pos.y, getPixel(originalPixels, originalPos.x, originalPos.y));
}
} else {
// copy unaffected pixels from original to new image
setPixel(affectedPixels, pixel.pos.x, pixel.pos.y, getPixel(originalPixels, pixel.pos.x, pixel.pos.y));
}
}
I've struggled a lot to get it to this point and I'm quite happy with the result. Nevertheless, I have a small problem; jagged pixels. Compare the JS pinch with Gimp's:
I don't know what I'm missing. Do I need to apply another filter after the actual filter? Or is my algorithm wrong altogether?
I can't add the full code here (as a SO snippet) because it contains 4 base64 images/textures (65k chars in total). Instead, here's a JSFiddle.
One way to clean up the result is supersampling. Here's a simple example: https://jsfiddle.net/Lawmo4q8/
Basically, instead of calculating a single value for a single pixel, you take multiple value samples within/around the pixel...
let color =
calcColor(x - 0.25, y - 0.25) + calcColor(x + 0.25, y - 0.25) +
calcColor(x - 0.25, y + 0.25) + calcColor(x + 0.25, y + 0.25);
...and merge the results in some way.
color /= 4;
I asked this question earlier but didn't communicated it clearly, so forgive me for the duplicate. This should be better.
I need to figure the position of a coordinate, given three other coordinates and 2 slopes. Basically, the intersection point of two lines. However, I don't have all of the information normally available to solve this.
I have an arbitrary shape defined by a bunch of vertices. The user may drag a line between these vertices and the shape should react as the diagrams below show.
So in the first example, the user drags line EF from the position on the left to the position on the right (line E2F2). What needs to happen is that line EF grows/shrinks such that it's slope stays the same and that it's beginning and ending coordinates remain on the lines DE and AF respectively. This is shown as line E2F2.
This needs to be generic enough that it can handle any sort of strange or regular angles I throw at it. The second set of shapes shows a simpler approach. The user drags line CD to the position of C2D2. Notice how the slopes stay the same and D2 essentially slides down that diagonal line and B2C2 and C2D2 both extend in length. The result is that all 3 slopes stay the same but lines B2C2 and C2D2 grow in length to stay connected, while line D2E2 shrinks.
You'll need to understand that when dragging line EF, you're actually moving the coordinate "E". So, figuring the first coordinate is easy. And the previous and next one's never change. So I essentially have the slopes of the 3 relevant lines and 3 of the 4 necessary coordinates. I need the 4th, so in my example, F2 or D2.
This code is called on an event every time the coordinate moves. Lets say we're dragging line EF - the coordinate is E then.
var next = this.model.get("next"), // coordinate F
nextNext = next.get("next"), // coordinate A
nextDx = nextNext.get("x") - next.get("x"), // delta X of AF
nextDy = nextNext.get("y") - next.get("y"), // delta Y of AF
prev = this.model.get("prev"), // coordinate D
prevDx = prev.get("x") - this.model.get("x"), // delta X of DF
prevDy = prev.get("y") - this.model.get("y"), // delta Y of DF
selfDx = next.get("x") - this.model.get("x"), // delta X of EF
selfDy = next.get("y") - this.model.get("y"), // delta Y of EF
selfX = this.initialCoords.x + this.shape.getX(), // the new position of E
selfY = this.initialCoords.y + this.shape.getY(),
selfM, selfB, prevM, prevB, nextM, nextB, m, x, y, b;
// check for would-be infinities
if (selfDx == 0) {
// **** THIS WHOLE BLOCK IS CORRECT ****
// i'm vertical
// we can safely assume prev/next aren't also vertical. i think? right?
prevM = prev.get("slope");
prevB = prev.get("y") - prevM * prev.get("x");
var myX = selfX,
myY = prevM * myX + prevB;
this.model.set({
x: myX,
y: myY
});
nextM = next.get("slope");
nextB = next.get("y") - nextM * next.get("x");
var nextX = selfX,
nextY = nextM * nextX + nextB;
next.set({
x: nextX,
y: nextY
});
} else if (selfDy == 0) {
//***** THIS WHOLE BLOCK IS CORRECT **** //
// i'm horizontal
if (prevDx == 0) {
// prev is a vertical line
this.model.set({
y: selfY
});
} else {
prevM = prev.get("slope");
prevB = prev.get("y") - prevM * prev.get("x");
var myY = selfY,
myX = (selfY - prevB) / prevM;
this.model.set({
x: myX,
y: myY
});
}
if (nextDx == 0) {
// next is a vertical line
next.set({
y: selfY
});
} else {
nextM = next.get("slope");
nextB = next.get("y") - nextM * next.get("x");
var nextY = selfY,
nextX = (selfY - nextB) / nextM;
next.set({
x: nextX,
y: nextY
});
}
} else {
// HELP HERE - you've chosen to drag an arbitrarily angled line. Figure out the "next" coordinate given the "current" one.
selfM = this.model.get("slope");
selfB = this.model.get("y") - this.model.get("slope") * this.model.get("x");
if (selfM < 0) {
prevM = prev.get("slope");
prevB = prev.get("y") - prevM * prev.get("x");
var myY = selfY,
myX = (selfY - prevB) / prevM;
// CORRECT, but need "next" position based on this
this.model.set({
x: myX,
y: myY
});
} else {
// CORRECT but need "next" position based on this.
var myX = selfX;
this.model.set({
x: myX
});
}
}
I had a similar situation and had some success using this page as reference :
http://en.wikipedia.org/wiki/Line-line_intersection
You should be able to enumerate all your lines testing for any points where they cross your moving line. These will be the new coordinates.
The equations in the wiki article assume lines of infinite length which you should be aware of but should actually be what you want (I think - there are probably edge cases).