Navigation for the ZIM JavaScript Canvas Framework

AboutExamplesLearnEditorCodeDocsDevsGold
ART CERTIFICATE

INSTRUCTIONS

1. Follow the steps below
2. Send your code to Dr Abstract on our Forum or Discord
3. Receive your digital certificate!
4. Share your experience
5. Get MORE certificates!




PART 1
TOP

Use a template and start coding!

1. Copy and paste the code below into a text document on your computer - save as art.html. We like using VS Code which is a free text editor with good code syntax coloring. HIGHLIGHT MORE
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <title>ZIM - Art Certificate - Code Creativity</title>
    
    <!-- zimjs.com - JavaScript Canvas Framework -->
    <script type=module>
    
    import zim from "https://zimjs.org/cdn/014/zim";
    
    // See Docs under Frame for FIT, FILL, FULL, and TAG
    new Frame(FIT, 1024, 768, light, dark, ready);
    function ready(frame, stage, stageW, stageH) {
        
        // given F (Frame), S (Stage), W (width), H (height)

        // Note: the Certificates were made before the F, S, W, H globals 
        // so we have collected the earlier frame, stage, stageW, stageH variables above

        // put code here
        
            
    } // end ready
    
    </script>
    <meta name="viewport" content="width=device-width, user-scalable=no" />
    </head>
    <body></body>
    </html>
                        

2. After it says put your code here, make a variable (const) called circle and use the new keyword to assign (=) a new Circle() with a radius of 50 and a color of pink.
Click on any of the green links to see the docs about the feature and show you how to use it. Here we need to pass extra information to the new Circle( ) when we make it. We pass the extra information in as parameters separated by commas (,). In this case, we need to put the values 50 and pink in the brackets of Circle( ) and separate them with a comma. The color could be any HTML color such as "blue", "#333", etc. (note the quotes) but we have stored a bunch of global colors (without quotes) so we often use those.
On the end of this statement, use a dot (.) to chain the circle's center() method which adds the circle to and centers the circle on the stage.
There is a helpful LEARN section on ZIM but we will tell you some tips in the grey dashed sections (like this one) as we go. Here is tip: a method does something (it is like a verb). To use a method, you put the object first (in this case the object is the new circle( )) then a period (.) and then the method with round brackets. We call this dot syntax because a dot separates the object and the method. Chaining is a technique that can be used with ZIM methods. See the MORE link for a video about Chaining.
HIGHLIGHT MORE ANSWER
3. Chain the circle's drag() method to make the circle draggable. Test the app in a Browser by finding the page on your computer and dropping it on an open Browser. (In Atom, you should get the open-in-browser package) HIGHLIGHT MORE ANSWER
PART 2
TOP

Use a loop and random numbers to create art!

4. Comment out the previous circle code using // at the start of the lines (or CTRL / in ATOM).
Hahaha - wasn't that productive! We are about to use the power of code to do our work for us and make 100 circles!
HIGHLIGHT MORE ANSWER
5. Make a variable (const) called circles and assign (=) a new Container() with a width value of stageW and a height value of stageH. Then addTo() the container to the stage.
Containers are objects that are used to hold Display objects. You cannot see the container but you can see the objects inside. The advantage is that you can move the container around and all its contents will move. You can remove a container and all its contents are removed. You can add an event to a container and its children will trigger the event. You can add a drag to a container and its children can individually be dragged.
HIGHLIGHT MORE ANSWER
6. Use loop() to loop 100 times and each time call an arrow function ( )=>{ } with a zog() of "hello" inside.
A zim loop( ) is like a for loop in JavaScript with a slightly easier syntax. It has three different modes to handle looping through Numbers, Arrays and Containers. In this case we put a Number 100 as the first parameter and a function literal (anonymous function) as a second parameter. The function will be called 100 times. The function literal receives parameters such as the index number of the loop but we are not using these in this case.
HIGHLIGHT MORE ANSWER
7. Inside the arrow function from step 6, create a new Circle() with a radius of 50, a color of pink (for now) and a borderColor of blue.
To make art, we want to vary the properties of objects often either sequentially or randomly. In this case we will use randomness with the zim rand() function. This is just like Math.random( ) but with a helpful format - you can pass rand( ) two numbers and it will pick a number between them. Optionally, if you pass one number, it picks between 0 and the number. There are other features of rand( ) too that you can read about in the docs. We will start off positioning the circles in random x and y positions on the stage.
Chain a loc() to locate the circle at an x, y in a container (or the stage if no container is provided). For the x use a rand() of stageW and for the y, use a rand() of stageH. For the container, use circles (NOT the stage). In this case, we do not need to store the circle in a variable. They will just become children of the circles container. HIGHLIGHT MORE ANSWER
ZIM Learn References
8. Go back and adjust the radius of the Circle() to a random number between 20 and 100 using rand(). Also chain on an alp() method with a value of a random number between .1 and .5 to randomize the transparency. HIGHLIGHT MORE ANSWER
9. ABOVE the loop code, create a const called colors and assign (=) an Array that holds pink, purple, blue and yellow. Then replace the Circle's pink (color parameter) with colors. This will make ZIM pick a color from the array each time a circle is made!
In ZIM VEE (5) we introduced dynamic parameters (parameters that change values inside the function). So we refer to the values as ZIM VEE values.
  • An array [1,3,2] - will pick randomly
  • An object {min:10, max:20} - will pick from the range
  • A series(1,2,3) - will pick in order
  • A function(){return result} - will pick the result
  • HIGHLIGHT MORE ANSWER
    10. Chain on a animate() with a single configuration object { } as a parameter (ZIM Duo Technique). The configuration object should have an props property of {scale:2}, a time property of 2, a loop property of true and a rewind property of true. This will animate all the circles bigger and smaller which will not look very good - but we will fix it in the next step.
    The ZIM DUO technique (introduced in ZIM DUO (2)) is when you can pass in parameters in the regular orderly way or pass in a single parameter that is a configuration object. This is just an object literal { } that has properties that match the parameter names. This allows you to skip parameters you do not need and to not worry about the order. The drawback is that you have to type the parameter names.
    HIGHLIGHT MORE ANSWER
    11. Change the scale property of the obj property of the animate() configuration object to an Array [ ] of options (ZIM VEE) such as .1, .5, .7, 1.3, 1.5, 2. This is tricky because we do not want a random number from a range as that would give us numbers close to 1 which would leave circles unchanging and looking awkward. Also change the time property to a ZIM VEE object { } with a min property of 5 and a max property of 10. This sets random times between 5 seconds and 10 seconds. HIGHLIGHT MORE ANSWER
    12. On the circles container, call its drag() method.
    Putting a drag( ) on a container will make it so any object within the container will be draggable. We could make the whole container drag by setting the all parameter to true but we do not want to.
    HIGHLIGHT MORE ANSWER
    ZIM Learn References
    This is the last step of our first art piece! In the next parts we will make three more art pieces and controls to go from one piece to the other. When we do this, we transition between pieces with a page slide. During this page slide, the art piece is cached which is like a screen capture. If the animation continues "behind the scene" then when we come back to the art piece, there will be a jumping to the art piece at a different time. This is not good. So here is a way to pause the animation of the art. We will turn off and on the animation in the page controls in the next part.
    13. In the configuration object { } of the animation() add an id property with a value of "circles". AFTER our drag( ) code, use a pauseAnimate() function with first parameter of true and a second parameter of "circles". This will pause only the animations with the id of circle. If we left out the id then all animations would pause including our future page transition. HIGHLIGHT MORE ANSWER
    PART 3
    TOP

    Create pages with a navigation bar

    In this part, we will make our navigation at the BOTTOM of the template. In the last three parts we will add our art pieces ABOVE the navigation and keep adding to the navigation as we go.
    14. Just ABOVE the stage.update( ), make a variable (const) called pages and assign (=) a new Pages() object. Add an Array [ ] with our pages as the first parameter - currently, we have one page: circles - so add circles to the array. As a second parameter, add "slide" which is the type of transition and as a third parameter, add 1 for the transition speed in seconds. HIGHLIGHT MORE ANSWER
    15 a) Make a variable (const) called nav and assign (=) a new Container() with a width of stageW and a height of 100. Chain on a pos() and pass in 0 and 0 as the x and y parameters and then LEFT and BOTTOM as sides.

    15 b) Make a variable (const) called backing and assign (=) a new Rectangle() with a width of nav.width and a height of nav.height. The color will default to black. Chain on an addTo() and pass nav as the parameter (NOT stage). Chain on an alp() with .5 as the parameter.

    15 c) So that we cannot click through the nav and drag the circles we can put a mousedown event on the backing that calls an empty function. On the next line (do not chain!), add an on( ) method to the backing with "mousedown" as the first parameter and an empty arrow function ( )=>{ } as the second parameter. Test in a browser to see our panel. HIGHLIGHT MORE ANSWER
    16 a) We will make arrow buttons to go from page to page. Note: in the video we have one arrow but we will make two for next and previous arrows. Make a variable (const) called next and assign (=) a new Arrow() with white, yellow and pages as parameters. Adding pages as a parameter will hook up the arrow to the Pages object from step 14. Chain on an pos() 50, 0, RIGHT and CENTER as parameters. Chain on an alp() with .7 as the parameter.

    16 b) Make a variable (const) called prev and assign (=) a new Arrow() with white, yellow, pages and LEFT as parameters. LEFT will make the pages go to the left. Chain on an rot() 180 as a parameter to flip the arrow to the left. Chain on an pos() 50, 0, LEFT and CENTER as parameters. Chain on an alp() with .7 as the parameter. HIGHLIGHT MORE ANSWER
    ZIM Learn References
    17. ABOVE the navigation, make a variable (var) called rectangles and assign (=) a new Container() with a width of stageW and a height of stageH. Chain on an alp() with a parameter of .8 (we will blend in our page a bit with the background color) Add rectangles to the start of the Pages array before circles as this will be our next art piece! By default, Pages will put the first page in the array on the stage so we do not have to add it ourselves. HIGHLIGHT MORE ANSWER
    18. BENEATH the pages add an on( ) method to pages to capture a "page" event and call an arrow function ( )=>{ }. Inside the function, use a conditional if ( ) { } to find out if the lastPage property of pages is equal to (==) circles. If it is equal to circles then pauseAnimate() with parameters of true and "circles" to pause the circle animation now that we are leaving the page. We can't test quite yet until we do the next step. HIGHLIGHT MORE ANSWER
    We want to turn the circles animation on once the transition to the circles page is finished. Otherwise, if we animate during the transition, it is not seen due to the caching (screen shot) during transition and when the transition is done, the circles will be in a different position and there will be an undesired jump.
    19. BELOW the page event code, use the on( ) method of the pages to capture a pagetransitioned event and call an arrow function ( )=>{ }. INSIDE the function use a conditional if ( ) { } to test if the page property of pages is equal to (==) circles. If it is then use pauseAnimate() with parameters of false and "circles" to start the circles animating now that the transition is complete.

    View in a Browser and now the empty rectangles page should transition to the circles page when you press the next arrow button. Pressing the prev arrow button transitions to the empty rectangles page, etc. In the next part, we will complete the rectangles page. In the two parts after that, we will make two more pages and add them to the navigation as we go! HIGHLIGHT MORE ANSWER
    PART 4
    TOP

    Create art using noise

    For the rectangles art piece we will use noise. ZIM Noise() has an equation (OpenSimplex) that looks random but is actually a weirdly wiggly line, or plane or structure. See the MORE section for related links with explanations and examples. In this case, we have a number of tall rectangles that we animate with noise. We use 2D noise and set the height of the rectangles to the result of the noise. We pass in the rectangle number (adjusted) as the first parameter of the noise. We animate the second parameter of the noise inside a ZIM Ticker(). We will use two Slider() objects to control properties of the noise.
    20. BELOW the rectangles code, make a variable (const) called noise and assign (=) a new Noise() object. HIGHLIGHT MORE ANSWER
    21 a) Prepare to make Rectangles in a loop. We could use a Tile() for this in one line! But let's go old school. Make a variable (const) called greys and assign (=) an Array [ ] with the following values: light, lighter, dark, darker, silver, tin, grey. Set a variable called margin to 100. Set a variable called total to 20. Set a variable called width to (stageW-margin*2)/total. This will divide up our space (less the margins on each side) into the width for each rectangle. Set a variable called height to stageH/2.

    21 b) Create a loop() and pass in total and an arrow function i=>{ } as parameters. Note that i will increase from 0 each time the loop runs - the format is simplified from (i)=>{ }

    21 c) INSIDE the loop create a new Rectangle() with the following parameters: width, height, greys, black, 1, width/2. This will make rectangles of random shades of grey with a black border and rounded corner (the last parameter). Chain on a reg() and pass 0, height/2 as parameters. Chain on a loc() with margin+i*width, stageH/2-50, rectangles as parameters. This will place the rectangles next to one another across the stage but in the rectangles container. HIGHLIGHT MORE ANSWER
    Here we will use a Ticker.add() to animate the rectangles. We will keep track of an increasing number, num, to animate the noise value. Each time we will increase num by a small amount, speed. And we will apply a factor, zoom, to the first parameter of noise to say how close we are zooming in on the noise equation. A higher zoom makes smaller steps and therefore a smaller difference in the rectangle heights.
    22 a) Set a variable (let) called num to 0. Set a variable (let) called speed to .05. Set a variable (let) called zoom to 20.

    22 b) Create a variable (const) called ticker and store the result of the add( ) static method of a Ticker. Pass in an arrow function ( )=>{ } as its parameter. This is the function that will run constantly at the framerate - probably 60 times a second. We store the result of the add() method this time so that we can add and remove the function later.

    22 c) INSIDE the Ticker function add speed to the num using the addition assignment operator (+=). Then use loop() as a method of rectangles and pass an arrow function (rect, i)=>{ } as the parameter. This means, each time we will get the rectangle in rectangles and the index number starting at 0.

    22 d) INSIDE the loop function make a variable (let) called result hold the result of the simplex2D(*nbsp;) method of noise. Pass in i/zoom, num as parameters for simplex2D. Set the heightOnly property of the rect to stageH/2 + result*200.
    The first parameter of the simplex2D( ) specifies where on the noise equation to look for a value. The second parameter moves this location in a second dimension which changes it over time (as we increase num). The result of simplex2D( ) will always be a number from -1 to 1. The heightOnly value is determined from the starting height of the rectangles (stageH/2) plus the noise value (from -1 to 1) multiplied by 200. So we will end up with the original height plus or minus 200.
    HIGHLIGHT MORE ANSWER
    In this step and the next, we are going to set up controls for the speed and zoom values using a Slider(). These will range from .001 to .1 and from 30 to 1. Sliders can also use bigger numbers as their min (left) and smaller numbers as their max (right). We will add the sliders to the nav.
    23 a) At the bottom, BEFORE the last stage.update( ) and AFTER the prev arrow button code, make a variable (const) called slider and assign (=) a new Slider() with a single configuration object { } as its parameter. The configuration object should have a min property of .001, a max property of .1 and a barColor property of silver and a currentValue property of speed. Chain on a center() and pass the nav as a parameter (NOT stage). Chain on an mov() with a parameter of -200. Set the alpha property of the button property of the slider to .5.

    23 b) Use the on( ) method of the Slider() to set a "change" event and call an arrow function ( )=>{ }. INSIDE the function set speed to the slider's currentValue property using the assignment operator (=). HIGHLIGHT MORE ANSWER
    24 a) Make a variable (const) called slider2 and assign (=) a new Slider() with a single configuration object { } as its parameter. The configuration object should have a min property of 30, a max property of 1 and a barColor property of silver and a currentValue property of zoom. Chain on a center() and pass the nav as a parameter (NOT stage). Chain on an mov() with a parameter of 200. Set the alpha property of the button property of the slider2 to .5.

    24 b) Use the on( ) method of the Slider() to set a "change" event and call an arrow function ( )=>{ }. INSIDE the function set zoom to the slider2's currentValue property using the assignment operator (=). Try out the code in a Browser and adjust the Sliders! Cool! HIGHLIGHT MORE ANSWER
    We have a problem transitioning while animating the rectangles that is similar to the problem with transitioning the animated circles. So we will want to turn off the Ticker and let the navigation turn it on when needed. We do not turn off the Ticker right away as we want to set the rectangles to a random state first rather than all in a straight line.
    25. BELOW the Ticker function, use timeout() with a first parameter of .5 for half a second and a second parameter of an arrow function ( )=>{ }. INSIDE the function use the remove( ) static method of the Ticker and pass ticker as the parameter. This will stop the rectangle animation after half a second. HIGHLIGHT MORE ANSWER
    26. Prepare for the next part. UNDER the timeout create a variable (const) called mosaic and assign (=) a new Container() with stageW and stageH passed in as parameters. ADD the mosaic to the start of the pages array below. Now, our app will start at the empty mosaic page. HIGHLIGHT MORE ANSWER
    27. We want to hide the sliders and let the nav take care of showing them. Chain on an alp() with a parameter value of 0 to each slider. HIGHLIGHT ANSWER
    28. Add an else if ( ) { } to the pagetransitioned event function conditional. Test if the page property of pages is equal to (==) rectangles then use the add() method of the Ticker and pass ticker as the parameter value. This will turn back on the animation after the page transitions to the rectangles. Also fade in the sliders using the animate() method of each slider. The parameters for each will be {alpha:1} which will default to 1 second. HIGHLIGHT MORE ANSWER
    29. Add an else if ( ) { } to the page event function conditional. Test if the lastPage property of pages is equal to (==) rectangles Use the remove( ) method of the Ticker with a parameter of ticker to stop the rectangles from animating. Use the animate() method of each slider to animate the alpha out. The parameters for each will be {alpha:0}.
    Whew! We are now ready to go to the next part and make Mosaic Art!
    HIGHLIGHT MORE ANSWER
    PART 5
    TOP

    Use tiling and reflection to make art!

    For our mosaic art, we are going to make a tile container with a backing rectangle and some circles inside. We will then use Tile to tile the tile ;-). We will make it so that we can drag the circles in any tile and the rest of the tiles move to match!
    30. BELOW the mosaic code, make a variable (const) called tile and assign (=) a new Container() with 100 and 100 as width and height parameter values. Chain on a center() temporarily with a parameter of mosaic to see the tile on the screen. Make a variable (const) called rect and assign (=) a new Rectangle() with tile.width, tile.height, blue as parameters. Chain on an addTo() and pass in tile to add the backing to the tile. We do not want to drag the backing so chain on a noMouse(). HIGHLIGHT MORE ANSWER
    31. Make THREE Circle() objects - do not add them to variables. The first will have parameters of: 50, yellow, grey, 5. The second will have parameters of: 30, orange, purple, 4. And the last will have parameters of: 10, pink, purple, 3.. Chain on the center() method of each to center the circles on the tile (NOT the stage). Also chain on the setMask() method for each circle and pass in rect as the mask parameter. Now the circles when dragged will not go outside the shape of the rect which would look bad when tiled. HIGHLIGHT MORE ANSWER
    Here we will use a Tile() object to tile 8 columns and 4 rows of our tile. We can put a drag() on the Tile object and anything inside will drag (except the backings). We will make the matching circles move as we drag a circle and we will put a frame behind the art.
    32. Make a variable (const) called tiles and assign (=) a new Tile() with a configuration object { } as a single parameter. The configuration object should have properties as follows: obj property of tile, a cols property of 8, a rows property of 4, a rows property of 4, a spaceH property of -1, a spaceV property of -1, a mirrorH property of true, and a mirrorV property of true. This will tile 8 times across and 4 times down with spacing to get rid of small cracks and reflect every other tile horizontally and vertically. Chain on an center() and pass in mosaic to add the tiles to the page. Chain on an mov() and pass in 0, -50 to move the tiles up. Chain on a drag() with a single configuration object { } as a parameter having a property of onTop set to false. Now the circles will maintain their stacking order so the smaller ones never go behind the larger ones. IMPORTANT - remove the center() method from the original tile as we no longer need to do that. HIGHLIGHT MORE ANSWER
    33. Make a new Rectangle() - do not store it in a variable. Set the parameters to tiles.width+50, tiles.height+50, black. Chain on an center() and pass in mosaic and 0 as the parameters to add the rectangle behind the tiles. Chain on an alp() and pass in .1 to fade the rectangle out. Chain on a mov() with parameters of 0 and -50 to match where we moved the tiles. HIGHLIGHT ANSWER
    As we move a circle, we want the same circle in the other tiles to move at the same time. We can capture a pressmove event and then find the depth of the master circle we are moving. We can then loop through all the tiles and set the circle at that depth to the position of the master circle.
    34 a) Use the on( ) method of the Tile() to capture a "pressmove" event and call an arrow function e=>{ }. Note that we collect the event object as e in the arrow function - we used the short form of (e)=>{ }. This will give us extra information about the event such as the target inside the tile that caused the event. INSIDE the function, make a variable (let) called master and assign (=) the target property of the event object, e. This refers to the circle we are dragging.

    34 b) Make a variable (let) called level and store result of the level property of master - this is its level in its parent container.

    34 c) Use the loop() method of tiles and pass in an arrow function (tile)=>{ }. This means that we loop through the tiles and each time are given a tile in the parameter, tile. INSIDE the function, use the loc() method of the result of the getChildAt() method on the tile passing in the level as the parameter for getChildAt(). Pass in the x and y properties of the master into the two parameters of the loc(). This sets the location of the circle at the same level as the master to the position of the master. Finally, call the update() method of the stage to update the stage. All this is inside the "pressmove" event function.
    Try out the code in a Browser and drag the circles about! In the next steps we prepare for the final part.
    HIGHLIGHT MORE ANSWER
    35. Prepare for the next part. UNDER the "pressmove" event create a variable (const) called blobs and assign (=) a new Container() with stageW and stageH passed in as parameters. ADD blobs to the start of the pages array below. Now, our app will start at the empty blobs page. HIGHLIGHT MORE ANSWER
    36. Test out your app - it should start on the blank blobs page and arrow to the other two pages and back!
    We are now ready to go to the next part to draw shapes with Blobs and save the changes!
    HIGHLIGHT
    ZIM Learn References
    PART 6
    TOP

    Make pictures with Blobs and save changes!

    In this last part we will make three Blob() objects that are interactive and have their states saved so the user's picture is saved. Blobs by default will show Bezier curves to be able to change the blob shape.
    37 a) BELOW the blobs code, make a variable (const) called blobColors and assign (=) a ZIM series() with the following parameters: purple, orange, blue. Each time we use this series, it will get the next color.

    37 b) Use a loop() and pass in 3 as the first parameter (to make three blobs) and an arrow function i=>{ }. The i parameter will increase 0, 1, 2. INSIDE the function, make a variable (let) called blob and assign (=) a new Blob() with the following parameters: the blobColors, null, null (for the borderColor and borderWidth) and i+3 (to set the number of points on the blobs to 3, 4 and 5). Chain on a center() method with blobs as the parameter (NOT stage). Chain on a mov() method with a parameter of 300*(i-1). This will spread the blobs out across the page at -300, 0 and 300 from the center.

    37 c) Chain onto the blob, the ble() method with a parameter of "difference". This will mix the colors as the blobs overlap - of course, this is optional. HIGHLIGHT MORE ANSWER
    38. Make a new TransformManager() (transforms are position, rotation, scale and skew) with the following parameters blobs.children and "badges". The first parameter passes an array of the blobs to the TransformManager to tell it what to manage. The second parameter is an id which will make the TransformManager remember the transforms and save the shapes of our Blobs. Try changing the blobs and refreshing the page. The blobs will stay how they were!
    You can store the TransformManager in a variable - for instance const tm and then use tm.clear("badges") to clear remembered blobs and start over.
    HIGHLIGHT MORE ANSWER
    ZIM Learn References
    39. ADJUST the Pages() array to start with circles - we will show the circles art first and then go to rectangles, mosaic, blobs. HIGHLIGHT ANSWER
    40. Now that we are showing circles first, go UP to the circles code and comment out (//) or delete the pauseAnimate() on circles. HIGHLIGHT MORE ANSWER
    CONCLUSION: The world of GEN ART - generative and interactive art is wondrous and wild! You can get lost in experimentation and even hone in on the fabric of life itself. One thing to think about... if you can make art, then perhaps provide controls so that others can make art as well. ZIM Ornamate is an example. ZIM provides a series of components to let you dynamically adjust properties. An example of some can be found at the NOISE Examples after choosing an example. ZIM Bits also has several starts to art projects.

    ZIM helps you code the Canvas. Pretty well any 2D art techniques that have been available to coders - in Flash, Processing, etc. can be used in ZIM. Of course, it is always best when you unleash your own imagination and CODE CREATIVITY.
    FINAL STEP: Send your code to Dr Abstract on Slack or Discord to receive a digital certificate! Dr Abstract won a Hamilton Arts Award in the Media Arts category for his interactive works - many were created in ZIM.