For my project, I created an animated children’s book. This project was really exciting to create as I was able to practice using simple and more complex animation. With having 10 pages, I focused on experimenting with different kinds of animations and page turning effects. All of the backgrounds were drawn by me! The animated objects were also drawn by me, or created in p5.js. For the sake of cleanliness and consistency, I decided that drawing the background images myself would be the best possible solution.
The code is structured by creating an array of functions that implements the functions by indexing through a page number counter.
Detailed page explanations below:
On page 4, the Giraffe suggests for the Lion to sleep with a spoon under his pillow to make it rain, so the user must move the spoon to the pillow. When the spoon is within the boundaries of the pillow, it flips to the next page.
On page 5, I chose to do a simple page turning animation where the user must click within the boundary of the page turning image. While not nearly as complex as other pages, it was necessary to add this page for the sake of the story!
On page 9, the animals are having a party so I created an object animation of balloons floating to the top of the page. When the array length (i.e # of balloons) reaches 10 balloons, it moves to the next page.
On the last page, the animals begin to dance and I use a frame animation to flip through images of the animals moving. It also begins to rain and I created an object animation of rain falling from the top of the canvas.
Since I created a lot of my own images, I decided to package this project on my computer and run it on a local server.
To run this program:
- Download .zip file and extract it to your computer
- Open a command prompt, and type cd “path to file” (ex: C:\Users\Cassie Howard\Downloads\cmhoward-final-project\template-all)
- Then connect to python “python -m http.server”
- After it connects, open up http://localhost:8000 in your web browser.
If you get confused on how to move to the next page, remember the different page turning implementations i.e clicking on the jittering objects, completing the animation, or simply waiting (i.e for ten balloons to appear).
Code to drag objects inspired by:
Draggable Example (written by Daniel Shiffman)
code below! (it’s not NOT working, you just can’t see all of the background images until you connect to a local server!)
//Cassandra Howard
//Section B
//cmhoward@andrew.cmu.edu
//Final Project
//IMAGE VARIABLES
var page1;
var page2;
var page3;
var page4;
var spoon;
var page5;
var page6;
var page7;
var page8;
var page9;
var page10;
var turn_page;
var tomato;
//PAGE COUNTER
var page = 0;
//LOOP THROUGH PAGES
var clickFunctions = [click1, click2, click3, click4, click5, click6, click7, click8, click9, click10];
var drawFunctions = [draw1, draw2, draw3, draw4, draw5, draw6, draw7, draw8, draw9, draw10];
//DRAW1 GLOBAL VARIABLES
//VERTICES POINTS OF SUN
var sunX = [40, 50, 60, 50, 80, 50, 60, 50, 40, 50, 20, 50, 40];
var sunY = [26.5, 53, 80, 53, 53, 53, 26.5, 53, 80, 53, 53, 53, 26.5];
//DRAW2 GLOBAL VARIABLES
//SET Y VALUE OF TOMATOES, INTIALIZE TOMATOES POSITIONS
var tomatoY1 = 315;
var tomatoY2 = 300;
var tomatoFalling = false;
//DRAW4 GLOBAL VARIABLES
//INTIALIZE SPOON VALUES
var draggingSpoon = false;
var rolloverSpoon = false;
var spoonX;
var spoonY;
var spoonW;
var spoonH;
var offsetSpoonX;
var offsetSpoonY;
//DRAW6 GLOBAL VARIABLES
//LOAD IMAGE FILES FOR FRAME ANIMATION, CREATE EMPTY ARRAY FOR IMAGES
//INTIALIZE COIN POSITION
var frames = [];
var filenames = [
"assets/coin_1.png",
"assets/coin_2.png",
"assets/coin_3.png",
"assets/coin_4.png"
];
var coinX = 50;
var coinY = 50;
var targetX;
var targetY;
var dx;
var dy;
var distanceFromCoinToTarget;
//DRAW7 GLOBAL VARIABLES
//INTIALIZE APPLE VALUES
var draggingApple = false;
var rolloverApple = false;
var appleX;
var appleY;
var appleW;
var appleH;
var offsetAppleX;
var offsetAppleY;
//DRAW8 GLOBAL VARIABLES
//INTIALIZE APPLE2 VALUES
var draggingApple2 = false;
var rolloverApple2 = false;
var apple2X;
var apple2Y;
var apple2W;
var apple2H;
var offsetApple2X;
var offsetApple2Y;
//DRAW9 GLOBAL VARIABLES
//CREATE EMMPTY ARRAY FOR BALLOONS
var balloons = [];
var bx;
var b;
var by;
//DRAW10 GLOBAL VARIABLES
//LOAD IMAGES FOR FRAME ANIMATION, CREATE EMPTY ARRAY FOR IMAGES
//CREATE EMPTY ARRAY FOR RAINDROPS
var frames2 = [];
var filenames2 = [
"assets/dance_1.png",
"assets/dance_2.png",
];
var raindrops = [];
var rx;
var ry;
//LOAD BACKGROUND IMAGES FOR EACH PAGE
function setup() {
createCanvas(480, 480);
page1 = loadImage("assets/page_1.jpg");
page2 = loadImage("assets/page_2.jpg");
tomato = loadImage("assets/tomato.png");
page3 = loadImage("assets/page_3.jpg");
turn_page = loadImage("assets/turn_page.png");
page4 = loadImage("assets/page_4.jpg");
spoon = loadImage("assets/spoon.png");
spoonX = 100;
spoonY = 200;
spoonW = 50;
spoonH = 50;
page5 = loadImage("assets/page_5.jpg");
page6 = loadImage("assets/page_6.jpg");
//PUSH IMAGES INTO EMPTY ARRAY
for (var i = 0; i < filenames.length; i++) {
frames.push(loadImage(filenames[i]));
}
//SET ANIMATION VARIABLES
coinX = width / 2;
coinY = height / 2;
targetX = coinX;
targetY = coinY;
page7 = loadImage("assets/page_7.jpg");
apple = loadImage('assets/apple.png');
//SET APPLE POSITION
appleX = 240;
appleY = 355;
appleW = 50;
appleH = 50;
page8 = loadImage("assets/page_8.jpg");
//SET APPLE2 POSITION
apple2X = 100;
apple2Y = 355;
apple2W = 50;
apple2H = 50;
page9 = loadImage("assets/page_9.jpg");
//CREATE BALLOON OBJECTS
for (var i = 0; i < 5; i++) {
var bx = random(0, width);
var b = random(50, 80);
var by = height;
balloons[i] = makeBalloon(bx, by, b);
}
page10 = loadImage("assets/page_10.jpg");
//PUSH IMAGES INTO EMPTY ARRAY
for (var i = 0; i < filenames2.length; i++) {
frames2.push(loadImage(filenames2[i]))
}
//CREATE RAINDROP OBJECTS
for (var i = 0; i < 5; i++) {
var rx = random(0, width);
var ry = 0;
raindrops[i] = makeRaindrop(rx, ry);
}
frameRate(5);
}
//CLICK THROUGH PAGES WITH CLICK FUNCTIONS
function mouseClicked() {
clickFunctions[page]();
}
//CALL PAGE FUNCTIONS
function draw() {
drawFunctions[page]();
}
//OBJECT DRAGGING FUNCTIONS
function mousePressed() {
//PAGE 4
if (mouseX > spoonX & mouseX < spoonX + spoonW && mouseY > spoonY && mouseY < spoonY + spoonH) {
draggingSpoon = true;
offsetSpoonX = spoonX - mouseX;
offsetSpoonY = spoonY - mouseY;
}
//PAGE 6
if (mouseX > appleX & mouseX < appleX + appleW && mouseY > appleY && mouseY < appleY + appleH) {
draggingApple = true;
offsetAppleX = appleX - mouseX;
offsetAppleY = appleY - mouseY;
}
//PAGE 7
if (mouseX > apple2X & mouseX < apple2X + apple2W && mouseY > apple2Y && mouseY < apple2Y + apple2H) {
draggingApple2 = true;
offsetApple2X = apple2X - mouseX;
offsetApple2Y = apple2Y - mouseY;
}
}
//PAGE 1
//MAKE SUN JITTER, CLICK SUN TO MOVE TO NEXT PAGE
function draw1() {
noStroke();
background('white');
image(page1, 0, 0, page1.width, page1.height);
var sunPoints = sunX.length;
fill(254, 192, 68);
beginShape();
for (var i = 0; i < sunPoints; i++) {
var sunpX = sunX[i] + random(-2, 2);
var sunpY = sunY[i] + random(-2, 2);
vertex(sunpX, sunpY);
}
endShape(CLOSE);
ellipse(50, 53, 10, 10);
}
function click1() {
if (mouseX < 60 & mouseX > 40 && mouseY < 60 && mouseY > 40) {
page = 1;
};
}
//PAGE 2
//MAKE TOMATOES JITTER, CLICK TOMATOES TO MOVE TO NEXT PAGE
function draw2() {
noStroke();
background('white');
image(page2, 0, 0, page2.width, page2.height);
if (tomatoFalling === true) {
tomatoY1 += 10;
tomatoY2 += 10;
} else {
tomatoY1 = random(350, 355);
tomatoY2 = random(300, 305);
}
image(tomato, random(260, 265), tomatoY1, 30, 30);
image(tomato, random(270, 275), tomatoY2, 30, 30);
image(tomato, random(315, 320), tomatoY1, 30, 30);
image(tomato, random(310, 315), tomatoY2, 30, 30);
image(tomato, random(360, 365), tomatoY1, 30, 30);
image(tomato, random(355, 360), tomatoY2, 30, 30);
if (tomatoY1 > page2.height & tomatoY2 > page2.height) {
page = 2;
}
}
function click2() {
if (mouseX < 365 & mouseX > 260 && mouseY > 300 && mouseY < 355) {
tomatoFalling = true;
}
}
//PAGE 3
//LOAD BACKGROUND IMAGE, CLICK PAGE TURN ANIMATION TO GO TO NEXT PAGE
function draw3() {
noStroke();
background('white');
image(page3, 0, 0, page3.width, page3.height);
image(turn_page, 430, 430, turn_page.width, turn_page.height);
}
function click3() {
if (mouseX < 480 & mouseX > 400 && mouseY < 480 && mouseY > 400) {
page = 3;
}
}
//PAGE 4
//COMPLETE DRAGGING SPOON TO MOVE TO NEXT PAGE
function draw4() {
noStroke();
background('white');
image(page4, 0, 0, page4.width, page4.height);
if (mouseX > spoonX & mouseX < spoonX + spoonW && mouseY > spoonY && mouseY < spoonY + spoonH) {
rolloverSpoon = true;
}
else {
rolloverSpoon = false;
}
if (draggingSpoon) {
spoonX = mouseX + offsetSpoonX;
spoonY = mouseY + offsetSpoonY;
}
image(spoon, spoonX, spoonY, spoonW, spoonH);
if (spoonX > 100 & spoonX < 200 && spoonY > 50 && spoonY < 100) {
page = 4;
}
}
function mouseReleased() {
dragging = false;
}
function click4() {
}
//PAGE 5
//LOAD BACKGROUND IMAGE, CLICK PAGE TURN ANIMATION TO FLIP TO NEXT PAGE
function draw5() {
noStroke();
background('white');
image(page5, 0, 0, page5.width, page5.height);
image(turn_page, 430, 430, turn_page.width, turn_page.height);
}
function click5() {
if (mouseX < 480 & mouseX > 400 && mouseY < 480 && mouseY > 400) {
page = 5;
}
}
//PAGE 6
//TOSS COIN INTO POND, USING FRAME ANIMATION, WHEN COIN REACHES POND, FLIP TO NEXT PAGE
function draw6() {
noStroke();
background('white');
image(page6, 0, 0, page6.width, page6.height);
dx = targetX - coinX;
dy = targetY - coinY;
distanceFromCoinToTarget = sqrt(dx*dx + dy*dy);
coinX = lerp(coinX, targetX, 0.1);
coinY = lerp(coinX, targetY, 0.1);
if (targetX < coinX) {
push();
scale(-1, 1);
image(frames[frameCount % frames.length], -coinX, coinY);
pop();
}
else {
image(frames[frameCount % frames.length], coinX, coinY);
}
if (coinY > 350) {
page = 6;
}
}
function click6() {
targetX = mouseX;
targetY = mouseY;
}
//PAGE 7
//DRAG APPLE TO LION, WHEN APPLE REACHES LION, FLIP TO NEXT PAGE
function draw7() {
noStroke();
background('white');
image(page7, 0, 0, page7.width, page7.height);
if (mouseX > appleX & mouseX < appleX + appleW && appleY > appleY && mouseY < appleY + appleH) {
rolloverApple = true;
}
else {
rolloverApple = false;
}
if (draggingApple) {
appleX = mouseX + offsetAppleX;
appleY = mouseY + offsetAppleY;
}
image(apple, appleX, appleY, appleW, appleH);
if (appleX > 75 & appleX < 125 && appleY > 300 && appleY < 350) {
page = 7;
}
}
function click7() {
}
//PAGE 8
//DRAG APPLE FROM LION TO OWL, WHEN APPLE REACHES OWL, FLIP TO NEXT PAGE
function draw8() {
noStroke();
background('white');
image(page8, 0, 0, page8.width, page8.height);
if (mouseX > apple2X & mouseX < apple2X + apple2W && apple2Y > apple2Y && mouseY < apple2Y + apple2H) {
rolloverApple2 = true;
}
else {
rolloverApple2 = false;
}
if (draggingApple2) {
apple2X = mouseX + offsetApple2X;
apple2Y = mouseY + offsetApple2Y;
}
image(apple, apple2X, apple2Y, apple2W, apple2H);
if (apple2X > 230 & apple2X < 300 && apple2Y > 150 && apple2Y < 200) {
page = 8;
}
}
function click8() {
}
//PAGE 9
//CREATE BALLOONS THAT RISE FROM THE BOTTOM OF THE SCREEN, WHEN 10 BALLOONS HAVE BEEN CREATED, MOVE TO NEXT PAGE
function draw9() {
background('white');
image(page9, 0, 0, page9.width, page9.height);
updateAndDisplayBalloons();
addNewBalloons();
if (balloons.length > 10) {
page = 9;
}
}
function updateAndDisplayBalloons() {
for (var i = 0; i < balloons.length; i++) {
balloons[i].move();
balloons[i].display();
}
}
function addNewBalloons() {
var newBalloonProb = .05;
var b = random(50, 80);
if (random(0, 1) < newBalloonProb) {
balloons.push(makeBalloon(random(0, width), height, random(50, 80)));
}
}
function balloonMove() {
this.y -= this.speed;
}
function balloonDisplay(){
push();
if (this.typeColor < 1) {
fill(255, 0, 0, 100);
}
if (this.typeColor > 1 & this.typeColor < 2) {
fill(0, 0, 255, 100);
}
if (this.typeColor > 2 & this.typeColor < 3) {
fill(128, 0, 128, 100);
}
ellipse(this.x, this.y, this.breadth/1.5, this.breadth);
stroke('black');
strokeWeight(1);
line(this.x, this.y + this.breadth/2, this.x, this.y + 100);
pop();
}
function makeBalloon(bx, by, b) {
var balloons = {x: bx, y: by, move: balloonMove, display: balloonDisplay, speed: 5, breadth: random(50, 80), place: random(50, 250), type: int(random(0, 3)), typeColor: random(0, 3)}
return balloons;
}
function click9() {
}
//PAGE 10
//CREATE RAINDROPS FALLING FROM TOP OF CANVAS
//USE FRAME ANIMATION TO MAKE ANIMALS DANCE
function draw10() {
background('white')
image(page10, 0, 0, page10.width, page10.height);
image(frames2[frameCount % frames2.length], 0, 180);
updateAndDisplayRaindrops();
addNewRaindrops();
}
function updateAndDisplayRaindrops() {
for (var i = 0; i < raindrops.length; i++) {
raindrops[i].move();
raindrops[i].display();
}
}
function addNewRaindrops() {
var newRaindropProb = .25;
if (random(0, 1) < newRaindropProb) {
raindrops.push(makeRaindrop(random(0, width), 0));
}
}
function raindropsMove() {
this.y += this.speed;
}
function raindropsDisplay(){
push();
fill(135, 206, 250, 100);
beginShape();
curveVertex(this.x, this.y);
curveVertex(this.x-5, this.y+15);
curveVertex(this.x, this.y+20);
curveVertex(this.x+5, this.y+15);
curveVertex(this.x, this.y);
endShape(CLOSE);
pop();
}
function makeRaindrop(rx, ry) {
var raindrops = {x: rx, y: ry, move: raindropsMove, display: raindropsDisplay, speed: 5, place: random(50, 250)}
return raindrops;
}
function click10() {
}
//END