import zim from "https://zimjs.org/cdn/016/zim";
// REFERENCES for ZIM at https://zimjs.com
// see https://zimjs.com/intro.html for an intro example
// see https://zimjs.com/learn.html for video and code tutorials
// see https://zimjs.com/docs.html for documentation
// see https://codepen.io/topic/zim/ for ZIM on CodePen
const assets = [
{font: "reuben", src: "Reuben.otf"},
"cathead.png", "catblink.png", "head_smaller.png"
];
const path = "https://assets.codepen.io/2104200/";
const frame = new Frame(FIT, 480, 600, lighter, black, ready, assets, path);
function ready() {
// given F (Frame), S (Stage), W (width), H (height)
// put code here
// CONTEXT
// This quiz was used for the ZIM Cat launch (a few years ago)
// https://zimjs.com/cat.html and press the cat in the top banner.
// For all versions, please see https://zimjs.com/about.html#versions
// Originally, this app handled a set of feature pages going to the left
// MENU PAGE
// ZIM CAT how has a Page class
// that is just a Container with a backing ;-)
// but it will stop people wondering how to make a page in ZIM!
const menu = new Page(W, H, yellow, green).addTo();
// Here is a container that will hold our text and arrows
// we will then animate this in with a sequence
// each child gets animated with a specified delay between
const info = new Container(menu.width, menu.height).addTo(menu);
const cat = new Container(W, H).center(menu).tap(()=>{
pages.go(certPages[0], "right");
});
const cathead = new Pic("cathead.png").sca(.9).pos(30, 0, LEFT, BOTTOM, cat);
const blink = new Pic("catblink.png").sca(.9).center(cat).loc(30, 184).alp(0);
const blinkSeries = series(1, 1, 2, 1, 1, 2, 1, 1, 1, 3, 1, 2, 2, 1);
interval({min: 7, max: 14}, function() {
// running right away so add a small delay here.
interval({min: 1, max: 3}, function() {
blink.alpha = 1;
S.update();
timeout({min: .17, max: .23}, function() {
blink.alpha = 0;
S.update();
});
}, blinkSeries());
}, null, true); // true is run right away
STYLE = {
color: dark,
Label: {
// series will apply these styles in order
lineHeight: series(40, 34),
size: series(38, 32),
align: CENTER,
font: "reuben",
variant: true // small caps
}
};
new Triangle(45, 45, 45).rot(90).loc(399, 422, info);
const labelRight = new Label("ZIM\n\n\nQuiz").hov(grey).loc(391, 341, info);
labelRight.on("mousedown", () => {
pages.go(certPages[0], "right");
});
STYLE = {}; // turn off the previous styles
// For this quiz example on codepen, we are only showing the quiz to right
// it was called a certifiCAT (get it)
const certPages = makeCertPages();
// NAVIGATION
// This looks slightly complex but the system handled
// 20 pages with 80 events and different transitions.
// To put ZIM page management into perspective,
// the code to handle going from a single page to a second page
// in xCode for iOS is probably longer than this code below
// https://www.flickr.com/photos/danzen/4425788955
// Also, since the Cat launch, we have added ZIM Arrow()
// Which works with ZIM Pages to automatically handle page to page
// hold links for all page arrows
const hotSpots = new HotSpots();
// Prepare pages for Page object
// This needs which pages will swipe to which
// The menu page added to start and will push other pages
// The menu swipes to the first certificate page on the right
const pageList = [
// page, then swipes for left, right, up, down
{page:menu, swipe:[null, certPages[0]]}
];
loop(certPages, (page, i) => {
const prev = certPages[i+1]?certPages[i+1]:menu;
const next = certPages[i-1]?certPages[i-1]:menu;
// Pages and swipes
pageList.push({page:page, swipe:[next, prev]});
// HotSpots - arrows
hotSpots.add(page,page.buttonLeft,function(){pages.go(next, LEFT);});
hotSpots.add(page,page.buttonRight,function(){pages.go(prev, RIGHT);});
hotSpots.add(page,page.cat,function(){pages.go(menu, UP, "pixelDark");});
});
const pages = new Pages(pageList, "bubbleZIM", .5, [
// set transition coming back to menu as "pixelDark"
// can specify transition to and from any page like this
[certPages[0], menu, "pixelDark"],
[certPages[certPages.length-1], menu, "pixelDark"]
]).addTo();
pages.puff(.01); // quickly add pages to stage to settle start positions
pages.on("page", function () {
// transform needs to be updated if manually moved - so just hide them
if (pages.lastPage == certPages[3] || pages.page == certPages[3]) {
certPages[3].rect.transformControls.hide();
}
});
pages.on("pagetransitioned", function () {
if (pages.page == certPages[3]) {
certPages[3].rect.transformControls.show();
}
});
function makeCertPages() {
const certPages = [];
// Ten page ZIM CAT Curio test for certifiCAT ;-)
// See header() and footer() functions at the very bottom
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PAGE 1
// all these could go right on the component
// we are just testing out the styles
STYLE = {
align: CENTER,
sound: true,
Selector: {
borderColor: lighter,
tile: new Tile(new Rectangle(50, 50, series(pink, green, blue)).centerReg(), 3, 1, 10, 10)
},
Slider: {
vertical: false,
useTicks: false,
accentSize: 12,
currentValue: 6
},
Indicator: {
backgroundColor: blue,
foregroundColor: pink,
interactive: true
},
Tabs: {
width: 150,
height: 50,
tabs: {noPick: [1, 2, 3]},
selectedBackgroundColor: blue,
selectedRollBackgroundColor: pink,
currentEnabled: true
},
Toggle: {
backgroundColor: pink.darken(.2),
toggleBackgroundColor: pink.lighten(.1),
startToggled: true
}
};
// ZIM CAT - Style static class
// The Style class holds a number of static methods to help organize STYLE
// STYLE is an object literal and can be manipulated like one at any time
// but just to make some things easier, Style was introduced with the following
// these methods are each just a couple lines of code to work on STYLE
// Style.clear() - clears the STYLE
// same as: STYLE = {};
// Style.clearTypes() - clears the types
// same as: delete STYLE.types;
// Style.clearGroups() - clears the groups
// same as: delete STYLE.groups
// Style.remembered - property for holding remembered styles
// same as: remembered = {};
// Style.remember(id) - stores a copy of the current style in memory at an id or "default"
// same as: remembered[id||"default"] = zim.copy(STYLE); // makes a copy and clones cloneable objects
// Style.clearRemembered(id) - clears all remembered styles
// same as: delete remembered[id||"default"];
// Style.recall(id) - sets the current style to a remembered STYLE
// same as: STYLE = remembered[id];
// Style.add(obj) - adds general styles in form {newStile:val, newStyle2:value}
// same as: STYLE.newStyle = val; STYLE.newStyle2 = val;
// Style.addType(typeName, obj) - adds a type along with its styles - overwrites the same type
// same as: STYLE.type.typeName = obj;
// Style.addGroup(groupName, obj) - adds a group along with its styles - overwrites the same group
// same as: STYLE.group.groupName = obj;
// Style.remove(styleName) - removes a single general style specified as a string - same as delete STYLE.styleName
// same as: delete STYLE.styleName;
// Style.removeType(typeName) - removes a type as string
// same as: delete STYLE.type.typeName
// Style.removeGroup(groupName) - removes a group as a string
// same as: delete STYLE.group.groupName
Style.remember("components"); // store current STYLE as "components"
// ZIM CAT - Tile with new unique parameter (inserted after spacings)
// true below means make the tile unique
// this will make sure items are not cloned which would lose any event
// and it atomatically uses an array in order
// note - if the unique was not set to true, an array would randomize as a ZIM VEE
// we wanted to provide a way for beginners to easily make a tile of unique objects
// and we did not want them to have to remember to use a series()
// So power users can still use ZIM VEE - just don't use unique and remember to set clone false
const page1 = header("Order the Component names", dark);
// ZIM CAT - wire() and wired() let you connect properties of objects
// usually, we are not connecting components
// and the default property happens to be the ZIM DEFAULTWIRE
// which means slider.wire(dial) will make the dial change as the slider changes
// but not the other way around until twoWay is set
const dial = new Dial();
const slider = new Slider().wire(dial, null, true); // true is twoWay
// if you want to wire twoWay but need to change the value in a filter
// for instance - setting the indicator to 3,4,5 sets the tabs to 0,1,2
// and visa versa... we could wire if only one way - but wiring two ways conflicts
// so use event listeners instead which know specifically which component was changed
const selector = new Selector().change(function() {
if (toggle.toggled) indicator.selectedIndex = selector.selectedIndex;
});
const indicator = new Indicator().change(function() {
if (toggle.toggled) {
if (indicator.selectedIndex < 3) selector.selectedIndex = indicator.selectedIndex;
else tabs.selectedIndex = indicator.selectedIndex - 3;
}
});
const tabs = new Tabs().change(function() {
if (toggle.toggled) indicator.selectedIndex = tabs.selectedIndex + 3;
});
const toggle = new Toggle();
new Tile([dial, slider, selector, indicator, toggle, tabs], 1, 6, 0, 40, true)
.sca(.6)
.pos(20, 0, LEFT, CENTER, page1);
Style.clear(); // or STYLE = {}
const tiles = [];
const keys = ["Dial", "Slider", "Selector", "Indicator", "Toggle", "Tabs"];
zim.loop(keys, function(letter, i) {
const tile = new zim.Label({
text: letter,
color: pink.lighten(.8),
font: "reuben",
variant: true,
size: 26,
backing: new zim.Rectangle(160, 40, new RadialColor([purple, pink], [0, 1], 0, 0, 150, 0, 0, 0), null, null, [30, 0, 30, 0]),
align: CENTER,
valign: CENTER,
});
tiles.push(tile);
});
const tile = new Tile({
obj: tiles,
unique: true,
cols: 1,
rows: 6,
spacingV: 16
});
// ZIM CAT - Scrambler Component
// https://zimjs.com/cat/scrambler.html
// scrambles any tile (even with multiple rows and cols)
const scrambler = new Scrambler(tile).pos(50, 5, RIGHT, CENTER, page1);
// Scrambler has a "complete" event for when done
// but here we just want to use the test() method to see if it is correct
// the TEST button is hooked up to call the test function on the page
// it is in the footer and will take care of the results Pane, etc.
page1.test = function() {return scrambler.test();};
footer(page1);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PAGE 2
const page2 = header("What version of ZIM\ncame just before CAT?", dark);
STYLE = {font: "reuben"};
const list = new List({
width: 300,
height: 300,
selectedIndex: -1,
selectedBackgroundColor: green,
selectedColor: purple,
list: ["ONE", "DUO", "TRI", "4TH", "VEE", "SIX", "HEP", "OCT", "NIO", "TEN", "LEV", "DOZ"]
}).center(page2);
page2.test = function() {
return list.text == "TEN";
};
footer(page2);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PAGE 3
const page3 = header("Arrange to make\na draggable circle!", dark);
const tiles3 = [];
const keys3 = ["new", "Circle", "()", ".", "drag", "()"];
zim.loop(keys3, function(letter, i) {
const tile = new zim.Label({
text: letter,
color: pink.lighten(.9),
size: 30,
backing: new zim.Rectangle(120, 120, blue),
align: CENTER,
valign: CENTER,
});
tiles3.push(tile);
});
const tile3 = new Tile({
obj: tiles3,
unique: true,
cols: 3,
rows: 2,
spacingH: 5,
spacingV: 5
});
const scrambler3 = new Scrambler(tile3, keys3, "text").center(page3);
page3.test = function() {return scrambler3.test();};
footer(page3);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PAGE 4
const page4 = header("What method is being used?", dark);
page4.rect = new Rectangle(100, 100, orange).centerReg(page4).mov(-100).transform({
borderColor: white,
borderWidth: 2,
handleSize: 16,
visible: false,
allowToggle: false
});
const buttons4 = new RadioButtons({
buttons: ["gesture()", "transform()", "drag()"],
vertical: true,
inherit: {color: white}
}).sca(.5).center(page4).mov(100);
page4.test = function() {return buttons4.text == "transform()";};
footer(page4);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PAGE 5
const page5 = header("Identify!", dark);
Style.add({
align: CENTER,
stickColor: white
});
Style.addType("Stepper", {
stepperType: "list",
continuous: true,
width: 400,
scale: .4,
backgroundColor: yellow,
arrowColor: yellow
});
const stepper5_1 = new Stepper(["Wobble", "Doodle", "Squiggle", "Wiggle", "Wave"]);
const stepper5_2 = new Stepper(["Blob", "Bean", "Putty", "Shaper", "Stresso"]);
new Tile([
new Squiggle({length: 230, thickness: 5}),
stepper5_1,
new Blob({color: blue, radius: 50, points: [[13, 30.8, 0, 0, -26.6, -25.4, 26.6, 25.4, "mirror"], [62, 9, 0, 0, -3.1, 27.2, 3.1, -27.2, "mirror"], [-12.1, -39.2, 0, 0, 44.7, -2, -44.7, 2, "mirror"], [-61.2, 28.3, 0, 0, -21.5, -18.2, 21.5, 18.2, "mirror"]]}),
stepper5_2
], 1, 4, 0, 30, true).center(page5); // true for unique
Style.clear();
page5.test = function() {return stepper5_1.currentValue == "Squiggle" && stepper5_2.currentValue == "Blob";};
footer(page5);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PAGE 6
const page6 = header("What is their name?", dark);
new Pic("pragma.png").sca(.5).center(page6).mov(0, -100);
Style.addType("Label", {
backing: new Rectangle(170, 60, orange),
align: CENTER,
font: "reuben",
color: white,
centerReg: true
});
const tile6 = new Tile([
new Label("Wanda"),
new Label("Pragma"),
new Label("Lula"),
new Label("Tiko")
], 2, 2, 20, 20, true);
Style.clear();
const selector6 = new Selector({tile: tile6, backgroundColor: clear}).center(page6).mov(0, 80);
page6.test = function() {return selector6.selectedIndex == 1;};
footer(page6);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PAGE 7
const page7 = header("ZIM parameters are like", dark);
STYLE = {color: white};
const buttons7 = new RadioButtons({
buttons: [
"(100, 100, null, null, null, 10)",
"({width:100, height:100, corner:10})",
"either - DUO"
]
}).rot(-30).sca(.5).center(page7).mov(10, -30);
new Rectangle({width: 100, height: 100, corner: 10}).alp(.8).pos(50, 150, RIGHT, BOTTOM, page7);
page7.test = function() {return buttons7.selectedIndex == 2;};
footer(page7);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PAGE 8
const page8 = header("Which hit test is best?", dark);
const bound = new Rectangle(400, 150, clear, fog, 1, 0, true).center(page8).mov(0, -60);
const rect = new Rectangle(80, 80, pink).center(page8).mov(-70, -60).drag(bound);
const circ = new Circle(40, blue).center(page8).mov(70, -60).drag(bound);
rect.on("pressmove", checkHit);
circ.on("pressmove", checkHit);
function checkHit() {
if (circ.hitTestCircleRect(rect)) {
if (rect.color == pink) {
rect.color = blue;
circ.color = pink;
}
S.update();
} else {
if (rect.color == blue) {
rect.color = pink;
circ.color = blue;
}
S.update();
}
}
STYLE = {
variant: false,
type: {
Stepper: {
stepperType: "list",
color: dark,
width: 700,
scale: .5,
vertical: true,
backgroundColor: yellow,
arrowColor: yellow
}
}
};
const stepper8 = new Stepper([
"hitTestPoint()",
"hitTestReg()",
"hitTestRect()",
"hitTestCircle()",
"hitTestCircleRect()",
"hitTestCircles()",
"hitTestBounds()",
"hitTestPath()",
"hitTestGrid()"
]).center(page8).mov(0, 110);
Style.clear();
new Label({
text: "Drag us!",
color: mist,
font: "reuben",
variant: true,
size: 26
}).loc(184, 125, page8);
page8.test = function() {return stepper8.selectedIndex == 4;};
footer(page8);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PAGE 9
const page9 = header("ZIM tool to reduce code", dark);
STYLE = {
font: "reuben"
};
const list9 = new List({
height: 300,
width: 320,
selectedIndex: -1,
viewNum: 4.5,
selectedBackgroundColor: green,
selectedColor: purple,
list: [
"ZIMON",
"ZIMIFY",
"WONDER",
"SOCKET",
"MVC",
"NPM",
"SHIM",
"DISTILL",
"TYPESCRIPT",
"ZAP",
"ZOO",
"ASSET LIST",
"CODE ZERO",
"BUBBLING",
"FRAME",
"ACCESSIBILITY",
"BASE",
"PIZZAZZ",
"ZIMPLIFY"
]
}).center(page9);
page9.test = function() {return list9.text == "DISTILL";};
footer(page9);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PAGE 10
const page10 = header("Who is Dr Abstract?", dark);
// lazy loading images to add dimensions if tiling
const tile10 = new Tile([
new Pic("abstract.png", 177, 177).centerReg(),
new Pic("frank.png", 177, 177).centerReg(),
new Pic("force.png", 177, 177).centerReg(),
new Pic("chris.png", 177, 177).centerReg(),
], 2, 2, 20, 20, true);
const selector10 = new Selector(tile10, pink, 4, clear, (177 + 20) / 2, [50, 10, 5, 10]).sca(.9).center(page10);
Style.addType("Label", {
color: green.darken(.1),
align: CENTER,
lineHeight: 16,
size: 24
});
new Label("d\na\nn\n\nz\ne\nn\n").loc(36, 151, page10);
new Label("f\nr\na\nn\nk\n\nl\no\ns").loc(442, 138, page10);
new Label("c\n\nc\no\ny\ni\ne\nr").loc(442, 329, page10);
new Label("f\nr\na\nn\nk\n\nf\no\nr\nc\ne").loc(36, 304, page10);
page10.test = function() {return selector10.selectedIndex == 0;};
footer(page10);
page10.buttonRight.visible = false;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// GENERAL FUNCTIONS
// overall test results go in here as true or false
const results = [];
function header(instructions, color) {
if (color == null) color = dark;
const page = new Page(W, H, green);
page.cat = new Pic("head_smaller.png").loc(14, -10, page);
F.makeIcon().sca(.3).pos(14, 50, RIGHT, TOP, page);
const backing = new Rectangle(480, 400, color).center(page);
new Label({
text: instructions,
align: CENTER,
valign: BOTTOM,
size: 24,
font: "reuben",
variant: true,
color: dark,
labelWidth: 400
}).loc(W / 2, 80, page).noMouse();
return page;
}
const levels = [
"CATastrophe",
"CATnapper",
"copyCAT",
"CATaloger",
"CATwalker",
"advoCAT",
"CATapulter",
"eduCATer",
"CATalyst",
"CATmando",
"certifiCAT"
];
function footer(page) {
Style.add({variant: true});
Style.addGroup("footer", {font: "reuben", color: purple});
// new Label("bot").pos(0,28,CENTER,BOTTOM,page);
const test = new Button({
width: 150,
height: 50,
backgroundColor: purple.lighten(.8),
rollBackgroundColor: purple,
color: purple,
rollColor: purple.lighten(.8),
corner: 8,
label: "TEST",
group: "footer"
}).pos(0, 20, CENTER, BOTTOM, page).tap(function() {
const result = page.test();
results[certPages.indexOf(page)] = result;
answer.text = result ? "CORRECT!" : "OOPS!";
let correct = 0;
let total = 0;
loop(results, function(r) {
if (zot(r)) return; // this is a ZIM loop continue
correct += r;
total += r == null ? 0 : 1;
});
standing.text = correct + "/" + total;
level.text = "Current Level: " + levels[correct] + "!";
pane.show();
});
page.buttonLeft = new Triangle(35, 35, 35, purple).rot(-90).pos(30, 30, LEFT, BOTTOM, page).expand();
page.buttonRight = page.buttonLeft.clone().rot(90).pos(30, 30, RIGHT, BOTTOM, page).expand();
certPages.push(page);
}
const pane = new Pane({
width: W + 200,
height: 200,
backgroundColor: new GradientColor([purple.lighten(.9), purple.lighten(.6)], [0, 1], 0, 0, 0, 200),
backdropColor: "rgba(0,0,0,.7)"
}).rot(-20);
const answer = new Label({text: "CORRECT!", size: 60, group: "footer"}).center(pane).noMouse().mov(-12, -20);
const level = new Label({text: "Current Level: CATastrophe!", size: 30, group: "footer"}).center(pane).noMouse().mov(-12, 40);
const standing = new Label({text: "0/1", size: 26, group: "footer", color: purple.lighten(.4)}).center(pane).noMouse().mov(200, -60);
return certPages;
}
} // end ready
// Docs for items used:
// https://zimjs.com/docs.html?item=Frame
// https://zimjs.com/docs.html?item=Pic
// https://zimjs.com/docs.html?item=Container
// https://zimjs.com/docs.html?item=Circle
// https://zimjs.com/docs.html?item=Rectangle
// https://zimjs.com/docs.html?item=Triangle
// https://zimjs.com/docs.html?item=Squiggle
// https://zimjs.com/docs.html?item=Blob
// https://zimjs.com/docs.html?item=Label
// https://zimjs.com/docs.html?item=Button
// https://zimjs.com/docs.html?item=RadioButtons
// https://zimjs.com/docs.html?item=Toggle
// https://zimjs.com/docs.html?item=Pane
// https://zimjs.com/docs.html?item=Page
// https://zimjs.com/docs.html?item=Indicator
// https://zimjs.com/docs.html?item=List
// https://zimjs.com/docs.html?item=Stepper
// https://zimjs.com/docs.html?item=Slider
// https://zimjs.com/docs.html?item=Selector
// https://zimjs.com/docs.html?item=Dial
// https://zimjs.com/docs.html?item=Tabs
// https://zimjs.com/docs.html?item=Scrambler
// https://zimjs.com/docs.html?item=tap
// https://zimjs.com/docs.html?item=change
// https://zimjs.com/docs.html?item=drag
// https://zimjs.com/docs.html?item=noMouse
// https://zimjs.com/docs.html?item=wire
// https://zimjs.com/docs.html?item=wired
// https://zimjs.com/docs.html?item=transform
// https://zimjs.com/docs.html?item=hitTestCircleRect
// https://zimjs.com/docs.html?item=loop
// https://zimjs.com/docs.html?item=pos
// https://zimjs.com/docs.html?item=loc
// https://zimjs.com/docs.html?item=mov
// https://zimjs.com/docs.html?item=alp
// https://zimjs.com/docs.html?item=hov
// https://zimjs.com/docs.html?item=rot
// https://zimjs.com/docs.html?item=sca
// https://zimjs.com/docs.html?item=addTo
// https://zimjs.com/docs.html?item=centerReg
// https://zimjs.com/docs.html?item=center
// https://zimjs.com/docs.html?item=expand
// https://zimjs.com/docs.html?item=Pages
// https://zimjs.com/docs.html?item=Arrow
// https://zimjs.com/docs.html?item=HotSpots
// https://zimjs.com/docs.html?item=Tile
// https://zimjs.com/docs.html?item=timeout
// https://zimjs.com/docs.html?item=interval
// https://zimjs.com/docs.html?item=copy
// https://zimjs.com/docs.html?item=GradientColor
// https://zimjs.com/docs.html?item=RadialColor
// https://zimjs.com/docs.html?item=series
// https://zimjs.com/docs.html?item=lighten
// https://zimjs.com/docs.html?item=darken
// https://zimjs.com/docs.html?item=STYLE
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.