I am pretty new to the whole basiljs world. So this might be a very basic question. I wasn't able to figure it out on my own however…Â
I am trying to create a simple script that swaps two Items that are selected on the same page.
I was able to get the image frame to swap, however it leaves the frames content in the same position. Here is waht it looks like:
#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";
function draw() {
var selItems = b.selections();
var selItems0x = b.itemX(selItems[0]);
var selItems1x = b.itemX(selItems[1]);
var selItems0y = b.itemY(selItems[0]);
var selItems1y = b.itemY(selItems[1]);
b.itemX(selItems[0], selItems1x);
b.itemX(selItems[1], selItems0x);
b.itemY(selItems[0], selItems1y);
b.itemY(selItems[1], selItems0y);
}
b.go();
Now my question is: How can I call on the frames content. Obviously I want that one the move the same with the frame.
Thanks for your help, I am eager to learn more!
Raphy
Even though it is not the "basiliy" way I suggest using InDesign build in functions. You can mix them with Basil Code. Basil doesn't care. There is the possibility to fit elements into its frame or center them.
Try this snippet:
#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";
function setup(){
var sel = b.selections();
var gb0 = sel[0].geometricBounds;
var gb1 = sel[1].geometricBounds;
// swap them
sel[0].geometricBounds = gb1;
sel[1].geometricBounds = gb0;
// see the different fit options
// http://yearbook.github.io/esdocs/#/InDesign/FitOptions
sel[0].fit(FitOptions.CENTER_CONTENT);
sel[0].fit(FitOptions.PROPORTIONALLY);
}
b.go();
The problem is that in InDesign a (image) graphic and its containing frame are treated as two separate objects, which means, if you only move the frame, the graphic in it does not move along.
In basil.js you have the method b.transformImage() to move a frame along with its graphic, but it is rather cumbersome to use, as you need to pass along the position as well as the scale of the image.
Alternatively you could move the graphic in a second step. Make sure first that the item actually contains a graphic (instead of being a simple oval etc.) and if that's the case move it to the same position as its parent frame. You can access a frames graphic by using frame.graphics[0].
The script would look like this then:
#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";
function draw() {
var selItems = b.selections();
var selItems0x = b.itemX(selItems[0]);
var selItems1x = b.itemX(selItems[1]);
var selItems0y = b.itemY(selItems[0]);
var selItems1y = b.itemY(selItems[1]);
b.itemX(selItems[0], selItems1x);
b.itemY(selItems[0], selItems1y);
if(selItems[0].graphics.length > 0) {
b.itemX(selItems[0].graphics[0], selItems1x);
b.itemY(selItems[0].graphics[0], selItems1y);
}
b.itemX(selItems[1], selItems0x);
b.itemY(selItems[1], selItems0y);
if(selItems[1].graphics.length > 0) {
b.itemX(selItems[1].graphics[0], selItems0x);
b.itemY(selItems[1].graphics[0], selItems0y);
}
}
b.go();
Note that this will not work if the top left corner of the image is cropped by the frame. In this case you would need to figure out where the top left corner of the graphic actually is and then offset the graphic accordingly after moving it.
Btw., the basil.js team is aware that the transforming of images is somewhat overly complicated and there is a plan to simplify this process in the future.
Related
NOTE: We need go with Viewbox concept ONLY !
I have a div(resize-div) in which I render SVG-polygon. I should be able to rearrange polygon points (by dragging and dropping them) or I can resize the resize-div.
Scenario:1 : I should be able to re-arrange polygon points by dragging them anywhere and resize-div(parent-div) will be rearranged/resized to accommodate all points within it. Which is working fine.
Scenario-2: Once I'm done with rearranging the polygon points, If I try to resize the resize-div(parent-div), the entire svg or some points flicker. OR in other word, after re-arranging points, If I double tap the resize-div(parent-div), the entire shape flickers.
In scenario-2, I use
arrVertexes = arrVertexes.map(item => {
return [(xScale.invert(item[0])), (yScale.invert(item[1])) ];
});
but for scenario-1 I don't use scale.invert.
The expected behavior is it should not flicker. It should work as is.
Been trying since last 2 weeks but no luck. Actual application is different. This is just a reproduction of what is happening in real-app.
DEMO - APP
function resize(){
arrVertexes = arrVertexes.map(item => {
return [(xScale.invert(item[0])), (yScale.invert(item[1])) ];
});
const eve = $('.resize-div').style;
eveWidth = $('.resize-div').width() - 2;
eveHeight = $('.resize-div').height() - 2;
if (width !== eveWidth) {
width = eveWidth;
}
if (height !== eveHeight) {
height = eveHeight;
}
getScaledPoints();
reload();
}
Not sure what is wrong. Tried to set preserveAspectRation(which I don't need) to none but it didn't work.
You code is a mess... I hardly understand what is going on there.
But one thing to mention. This code in getScaledPoints function:
return [Math.round(xScale(item[0])), Math.round(yScale(item[1]))];
Should be changed to:
return [xScale(item[0]), yScale(item[1])];
Do not make any rounding here! When you are working with "scaling", you should go into "real" numbers, not "integers". Example: click.
I'm a beginner on here, so apologies in advance for naivety. I've made a simple image on Brackets using Javascript, trying to generate circles with random x and y values, and random colours. There are no issues showing when I open the browser console in Developer Tools, and when I save and refresh, it works. But I was expecting the refresh to happen on a loop through the draw function. Any clues as to where I've gone wrong?
Thanks so much
var r_x
var r_y
var r_width
var r_height
var x
var y
var z
function setup()
{
r_x = random()*500;
r_y = random()*500;
r_width = random()*200;
r_height = r_width;
x = random(1,255);
y= random(1,255);
z= random(1,255);
createCanvas(512,512);
background(255);
}
function draw()
{
ellipse(r_x, r_y, r_width, r_height);
fill(x, y, z);
}
Brackets.io is just your text editor (or IDE if you want to be technical) - so we can remove that from the equation. The next thing that baffles me is that something has to explicitly call your draw() method as well as the setup() method -
I'm thinking that you're working in some sort of library created to simplify working with the Canvas API because in the setup() method you're calling createCanvas(xcord,ycord) and that doesn't exist on it's own. If you want to rabbit hole on that task check out this medium article, it walks you thru all the requirements for creating a canvas element and then drawing on that canvas
Your also confirming that you're drawing at least 1 circle on browser refresh so i think all you need to focus on is 1)initiating your code on load and 2)a loop, and we'll just accept there is magic running in the background that will handle everything else.
At the bottom of the file you're working in add this:
// when the page loads call drawCircles(),
// i changed the name to be more descriptive and i'm passing in the number of circles i want to draw,
// the Boolean pertains to event bubbling
window.addEventListener("load", drawCircles(73), false);
In your drawCircles() method you're going to need to add the loop:
// im using a basic for loop that requires 3 things:
// initialization, condition, evaluation
// also adding a parameter that will let you determine how many circles you want to draw
function drawCircles(numCircles) {
for (let i = 0; i < numCircles; i++) {
ellipse(r_x, r_y, r_width, r_height);
fill(x, y, z);
}
}
here's a link to a codepen that i was tinkering with a while back that does a lot of the same things you are
I hope that helps - good luck on your new learning venture, it's well worth the climb!
Thank you so much for your help! What you say makes sense - I basically deleted the equivalent amount of code from a little training exercise downloaded through coursera, thinking that I could then essentially use it as an empty sandpit to play in. But there's clearly far more going on under the hood!
Thanks again!
I have a page that is basically a large canvas with a lot of small icons connected with lines, and the user needs to be able to pan/zoom around. I've got everything working, but its very choppy. It seems that the repaining is the problem (if I remove the icons it becomes very smooth), but if I run Chrome's profiler, none of my functions are taking up any significant time at all.
Are there any better approaches to panning, without having to repaint everything? For instance in WinAPI, there was a function that scrolled the window content and only invalidated the thin region that just scrolled into view. Is there any way to do something similar in Javascript/canvas, since all I really need is to move the entire window?
I have tried making a giant canvas with everything pre-painted on it, that is then moved around with scrollLeft/scrollTop, but that takes way too much memory (what else should I expect from a 4000x4000 image) and makes zoom very slow instead.
Here's the page if anyone is interested, the code is pretty readable I hope:
http://poe.rivsoft.net/
You will have to just put up with some slower parts. Consider creating dirty regions. These are areas that need to be redrawn when panning. Keep a back buffer the same size as the canvas. When panning copy from the back buffer to its self the area that remains visible and mark the newly visible area as dirty. Then every frame rerender only the dirty areas onto the back buffer. For zooming you can zoom the back buffer and re render when the user pauses or incrementally, this will create a pixelated view (like google maps) when zooming in or aliasing and dirty areas on the sides when zooming out, until you update it.
You can also limit the amount of dirty area redrawn each frame so maintaining a constant frame rate. It will not look as nice but it will improve the panning and zooming. On my machine it runs well (nice job BTW) so you may want to consider implementing optimisations only on machines that can not handle the load.
Also looking at the function DrawNode there is lots of room for optimisation as you have a lot of redundant code (especially once all assets have loaded)
This is just a suggestion as I do not know if nodes are unique or if the x, y coords change, but that can be accommodated as well. You have a lot of searching and checks that should be avoided. The use of strings instead of numbers or booleans to check for status and type is also slow.
function DrawNode(ctx, node, x, y, active) {
// Has this node got quick render information
if (node.qNode) {
// if so render the quick version
var qn = node.qNode; // creating the var qn and then qn.? is quicker than access node.qNode.?
ctx.drawImage(qn.image, qn.coords.x, qn.coords.y, qn.coords.w, qn.coords.h, qn.x, qn.y, qn.size, qn.size);
return;
}
var type = NodeTypes[node.type];
var frameType = "frame" + (active ? "Active" : "Inactive"); // active should be a boolean
if (type && type.size && node.type !== "jewel") { // should be !node.isJewel with isJewwl a boolean
var spriteType = node.type;
if (node.type !== "mastery") // Should be boolean
spriteType += (active ? "Active" : "Inactive");
var sprites = SkillTree.skillSprites[spriteType][3];
var image = GetImage("Assets/" + sprites.filename);
var coords = sprites.coords[node.icon];
if (image && image.loaded && coords) {
ctx.drawImage(image, coords.x, coords.y, coords.w, coords.h,
x - type.size * 0.5, y - type.size * 0.5, type.size, type.size);
// add the information to quickly render the node next time.
// You may want to add sub objects for Mastery Active,inactive
node.qNode = {
image : image,
coords : coords,
x : x - type.size * 0.5,
y : y - type - sise * 0.5,
size : type.size
}
} else if (!image || !image.loaded) {
return false;
}
}
// same deal for the other type.
}
When optimising you start at the slowest point and make that code as efficient as possible, then work your way out. It is well written code but it has no eye for speed so I would say there is lots more room for improvement in the code.
To demonstrate this (I'm working on a much more compressed Fiddle), open this in chrome, and move Jay-Z using your arrow keys and catch about 4 - 5 (sometimes more!) cakes.
You will notice that there is a massive cupcake on the left side of the screen now.
I update the cakes' positions in my handleTick function, and add new cakes on a time interval. Here are both of those:
/*This function must exist after the Stage is initialized so I can keep popping cakes onto the canvas*/
function make_cake(){
var path = queue.getItem("cake").src;
var cake = new createjs.Bitmap(path);
var current_cakeWidth = cake.image.width;
var current_cakeHeight = cake.image.height;
var desired_cakeWidth = 20;
var desired_cakeHeight = 20;
cake.x = 0;
cake.y = Math.floor((Math.random()*(stage.canvas.height-35))+1); //Random number between 1 and 10
cake.scaleX = desired_cakeWidth/current_cakeWidth;
cake.scaleY = desired_cakeHeight/current_cakeHeight;
cake.rotation = 0;
cake_tray.push(cake);
stage.addChild(cake);
}
And the setInterval part:
setInterval(function(){
if (game_on == true){
if (cake_tray.length < 5){
make_cake();
}
}
else{
;
}
},500);
stage.update is also called from handleTick.
Here is the entire JS file
Thanks for looking into this. Note once again that this only happens in Chrome, I have not seen it happen on Firefox. Not concerned with other browsers at this time.
Instead of using the source of your item, it might make more sense to use the actual loaded image. By passing the source, the image may have a 0 width/height at first, resulting in scaling issues.
// This will give you an actual image reference
var path = queue.getResult("cake");
I'm wondering if there's a way to take existing images and "stack" them to create a single asset in Javascript.
http://imgur.com/a/ajkBh
The above image album shows what I'd like to do.
Basically, for the game I'm making, I want to procedurally generate enemy NPC's and the like, drawing from a pool of different body parts. Each potential body part would have stats and a spritesheet attached to it, so when the character is randomly generated, I want to stack all of the necessary images together into a single asset that I can then use.
Is there any way to do this?
Canvas is a very basic drawing API with the ability to draw a few basic shapes, strokes and fills. Other than filling with the background color, and/or clearing the whole canvas, there's no way to animate scenes with "sprites" or any complete objects sitting on top of each other, using only the basic canvas API. Copying images in is possible, but then you need to clear them every single frame and replace them, which is a lot of code overhead, if you want them to animate.
You should look into http://createjs.com or a similar "screen graph" type framework, something that sits on top of the canvas and lets you easily load up sprite sheets and move them around. It does the drawing, clearing, rotation, animation etc. of the canvas for you (basically making it a bit like Flash).
In terms of purely stacking or drawing on the canvas, yes you can grab an image and copy it directly onto the canvas using the context2d.drawImage method, but this is probably not going to achieve the effect you want by itself.
You can build up your animation out of existing parts, if think the main issue is organizing the base artworks and having the drawing done to fit one with another.
Let's say :
You want an idle (line 1), walk (line 2), run (line 3)
on each line you have a constant number of frames, say 5.
Say also that your parts are : legs, body, arms, head.
Then you have to build the image by yourself, by stacking those images :
function buildAnimation(legs, body, arms, head) {
var resImg = document.createElement('canvas');
resImg.width = legs.width; resImg.height = legs.height;
var resCtx = resImg.getContext('2d');
resCtx.drawImage(legs,0,0);
resCtx.drawImage(body,0,0);
resCtx.drawImage(arms,0,0);
resCtx.drawImage(head,0,0);
return resImg;
}
then you can feed your game framework with this image, that will be used for an
animation.
The drawback of this method is that you have to draw all animations of all parts
at the same places each time.
Issues :
1) for the head for instance, you might not want to animate it.
2) you might want different height for different characters.
3) it's a lot of work !!
So you might decide of conventions to know where the parts should be drawn, and have
less part to prepare in an image, but a more complex way to build them.
Short example : the file name of the image parts ends with their height, so you can retrieve them
easily. (bodyMonster48.png, bodyHead12.png, ...)
Writing everything would be too much work here, but just a short example :
say we have animWidth, animHeight the size of each anim, and five frames in each
of the 3 anims. Now we just have one head that we want to copy everywhere :
function buildAnimation(animWidth, animHeight, legs, body, arms, head) {
var resImg = document.createElement('canvas');
resImg.width = legs.width; resImg.height = legs.height;
var resCtx = resImg.getContext('2d');
resCtx.drawImage(legs,0,0);
resCtx.drawImage(body,0,0);
resCtx.drawImage(arms,0,0);
// copy the head in all frames of all anims
for (var animLine=0; animLine<3; animLine++) { // iterate in idle, walk, run
for (var animFrame= 0; animFrame<5; animFrame++) { // iterate in images of the animation
resCtx.drawImage(head, animFrame*animWidth, animLine*animHeight);
}
}
return resImg;
}
To be able to build any combination with variable height, you'll
have to carefully parametrize everything, use file naming and positioning conventions,
and you'll surely need a whole helper class not to get lost in all combinations.