coloron.php
Quell Code
<div id="alles">
<div class="splash"></div>
<div class="container">
<div class="start-game game-full-flex" id="start-game">
<div class="start-game-top"><a class="play-full-page" href="https://codepen.io/gregh/full/yVLOyO/" target="_blank">Full Page Mode</a></div>
<div class="logo-holder">
<p class="logo">
<span>C</span>
<span>o</span>
<span>l</span>
<span>o</span>
<span>r</span>
<span>o</span>
<span>n</span>
</p>
<a class="play-button" href="#" onclick="game.start()">Play</a>
<h4 class="hint">hint: <span>red</span> color always comes first</h4>
</div>
<div class="how-to-play">
<div class="section section-1">
<h4>Bouncing ball<br>changes color</h4>
<div class="content">
<div class="ball-demo" id="ball-demo"></div>
</div>
</div>
<div class="section section-2">
<h4>Tap on the bar to switch the colors<br>(Red, Yellow, Purple)</h4>
<div class="content">
<div class="bar bar-1" data-index="0"></div>
<div class="bar bar-2"></div>
<div class="bar bar-3"></div>
</div>
</div>
<div class="section section-3">
<h4>Always match the<br>ball and bar colors</h4>
<div class="content">
<div class="ball-demo" id="ball-demo"></div>
<div class="bar bar-1"></div>
</div>
</div>
</div>
</div>
<div class="stop-game game-full-flex">
<div class="score-container">
<h1>Coloron</h1>
<div class="final-score"></div>
<div class="result"></div>
<h4>Tweet to challenge friends</h4>
<p>
<a class="tweet" href="#" onclick="game.generateTweet()">
<i class="fa fa-twitter" aria-hidden="true"></i> Tweet
</a>
</p>
<div>
<a class="play-again" href="#" onclick="game.start()">Play Again</a>
<a class="main-menu" href="#" onclick="game.intro()">Menu</a>
</div>
</div>
</div>
<div class="small-glows"></div>
<div class="glow"><div class="sun"></div></div>
<div class="waves">
<div class="top_wave"></div>
<div class="wave1"></div>
<div class="wave2"></div>
<div class="wave3"></div>
<div class="wave4"></div>
</div>
<div class="mounts">
<div class="mount1"></div>
<div class="mount2"></div>
</div>
<div class="clouds"></div>
<div class="scene">
<div class="learn-to-play">Click on the bars to change the color!</div>
<div class="score" id="score"></div>
<div class="ball-holder"></div>
<div class="sticks" id="sticks"></div>
</div>
<div class="noise"></div>
</div></div>
<script>
class Game {
constructor() {
this.score = 0;
this.isRunning = 0; // game is not running
this.calculateScale();
this.timeline = new TimelineMax({ smoothChildTiming: true });
this.time = 1.6; // initial speed
this.colors = ["#FF4571", "#FFD145", "#8260F6"]; // the 3 colors used in the game
this.colorsRGBA = [
"rgba(255, 69, 113, 1)",
"rgba(255, 69, 113, 1)",
"rgba(255, 69, 113, 1)"];
this.color = this.colors[0]; // the intial color of the ball
this.prevColor = null; // used as a holder to prevent ball colors from repeating
}
/**
* The game screen is scalable. I took 1200x800px as the initial scale.
* In order to display the game an many screen sizes properly
* I have to compare the player's sreen size to the initial scale,
* then scale the game using CSS Transform to fit the screen properly
* The function is called in the controller and anywhere where I need
* to recalculate the scale on screen resize or device rotation
*/
calculateScale() {
this.screen = $(window).width(); // screen width
this.screenHeight = $(window).height();
this.scale =
this.screen > this.screenHeight ?
this.screenHeight / 800 :
this.screen / 1200;
this.stickWidth = 180 * this.scale;
this.steps = this.screen / this.stickWidth; // how many steps (stick width + margin) it takes from one end to another
}
/**
* Creating as many sticks we need to fill the screen
* from start to end of the screen. The steps property is used for that
*/
generateSticks() {
let numberOfSticks = Math.ceil(this.steps);
for (let i = 0; i <= numberOfSticks; i++) {if (window.CP.shouldStopExecution(0)) break;new Stick();}window.CP.exitedLoop(0);
}
generateBall() {
this.balltween = new TimelineMax({ repeat: -1, paused: 1 });
$(".scene .ball-holder").append('<div class="ball red" id="ball"></div>');
this.bounce();
}
generateTweet() {
let top = $(window).height() / 2 - 150;
let left = $(window).width() / 2 - 300;
window.open(
"https://twitter.com/intent/tweet?url=https://codepen.io/gregh/full/yVLOyO&text=I scored " +
this.score +
" points on Coloron! Can you beat my score?&via=greghvns&hashtags=coloron",
"TweetWindow",
"width=600px,height=300px,top=" + top + ",left=" + left);
}
/**
* The greeting when the game begins
*/
intro() {
TweenMax.killAll();
//TweenMax.to('.splash', 0.3, { opacity: 0, display: 'none', delay: 1 })
$(".stop-game").css("display", "none");
$(".start-game").css("display", "flex");
let introTl = new TimelineMax();
let ball = new TimelineMax({ repeat: -1, delay: 3 });
introTl.
fromTo(".start-game .logo-holder", 0.9, { opacity: 0 }, { opacity: 1 }).
staggerFromTo(
".start-game .logo span",
0.5,
{ opacity: 0 },
{ opacity: 1 },
0.08).
staggerFromTo(
".start-game .bar",
1.6,
{ y: "+100%" },
{ y: "0%", ease: Elastic.easeOut.config(1, 0.3) },
0.08).
staggerFromTo(
".start-game .ball-demo",
1,
{ scale: 0 },
{ scale: 1, ease: Elastic.easeOut.config(1, 0.3) },
0.8,
2);
ball.
fromTo(
".start-game .section-1 .ball-demo",
0.5,
{ y: "0px" },
{
y: "100px",
scaleY: 1.1,
transformOrigin: "bottom",
ease: Power2.easeIn }).
to(".start-game .section-1 .ball-demo", 0.5, {
y: "0px",
scaleY: 1,
transformOrigin: "bottom",
ease: Power2.easeOut,
onStart: () => {
while (this.prevColor == this.color) {if (window.CP.shouldStopExecution(1)) break;
this.color = new Color().getRandomColor();
}window.CP.exitedLoop(1);
this.prevColor = this.color;
TweenMax.to(".start-game .section-1 .ball-demo", 0.5, {
backgroundColor: this.color });
} });
}
/**
* Display score
*/
showResult() {
let score = this.score;
$(".stop-game").css("display", "flex");
$(".stop-game .final-score").text(score + "!");
$(".stop-game .result").text(this.showGrade(score));
$(".nominee").show();
let resultTimeline = new TimelineMax();
resultTimeline.
fromTo(
".stop-game .score-container",
0.7,
{ opacity: 0, scale: 0.3 },
{ opacity: 1, scale: 1, ease: Elastic.easeOut.config(1.25, 0.5) }).
fromTo(
".stop-game .final-score",
2,
{ scale: 0.5 },
{ scale: 1, ease: Elastic.easeOut.config(2, 0.5) },
0).
fromTo(
".stop-game .result",
1,
{ scale: 0.5 },
{ scale: 1, ease: Elastic.easeOut.config(1.5, 0.5) },
0.3);
}
/**
* Takes players score and generates the cheering copy
* @param {int} score
* @return {string} grade
*/
showGrade(score) {
if (score > 30) return "Chuck Norris?";else
if (score > 25) return "You're da man";else
if (score > 20) return "Awesome";else
if (score > 15) return "Great!";else
if (score > 13) return "Nice!";else
if (score > 10) return "Good Job!";else
if (score > 5) return "Really?";else
return "Poor...";
}
start() {
this.stop(); // stop the game
$(".start-game, .stop-game").css("display", "none"); // hide all the popups
$(".nominee").hide();
new Game();
this.score = 0; // reset
this.isRunning = 1;
// Clean up the stick and ball holders
// and generate new ones
$("#sticks, .scene .ball-holder").html("");
$("#score").text(this.score);
this.generateSticks();
this.generateBall();
// disables scene animations for Phones
if (
!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
window.navigator.userAgent))
{
Animation.sceneAnimation();
}
this.moveToStart();
this.moveScene();
// reset timescale to normal as the game speeds up
this.timeline.timeScale(1);
this.balltween.timeScale(1);
}
stop() {
this.isRunning = 0;
$(".start-game, .stop-game").css("display", "none");
$("#sticks, .scene .ball-holder, #score").html("");
TweenMax.killAll();
this.showResult();
}
scaleScreen() {
TweenMax.killAll(); // prevent multiple calls on resize
let height = $(window).height();
let width = $(window).width();
this.calculateScale();
$(".container").
css("transform", "scale(" + this.scale + ")").
css("height", height / this.scale).
css("width", width / this.scale).
css("transformOrigin", "left top");
$("#sticks").width(
this.screen / this.scale + 3 * this.stickWidth / this.scale);
}
/**
* Calls the above function
* If the game is running it stops and shows the score
* If the game has stops it takes player to the main menu
*/
scaleScreenAndRun() {
this.scaleScreen();
if (this.isRunning) {
this.stop();
} else {
this.intro();
}
}
/**
* This is the initial animation
* where the sticks come to the starting position
* and the ball appears and falls down
*/
moveToStart() {
let tip = new TimelineMax({ delay: 2 });
tip.
fromTo(
".learn-to-play",
1,
{ scale: 0 },
{ scale: 1, opacity: 1, ease: Elastic.easeOut.config(1.25, 0.5) }).
to(
".learn-to-play",
1,
{ scale: 0, opacity: 0, ease: Elastic.easeOut.config(1.25, 0.5) },
3);
TweenMax.fromTo(
"#ball",
this.time,
{
scale: 0 },
{
scale: 1,
delay: this.time * (this.steps - 3 - 1.5),
onComplete: () => {
this.balltween.play();
} });
this.timeline.add(
TweenMax.fromTo(
"#sticks",
this.time * this.steps,
{ x: this.screen / this.scale },
{ x: 0, ease: Power0.easeNone }));
}
/**
* The animation that moves sticks
*/
moveScene() {
this.timeline.add(
TweenMax.to("#sticks", this.time, {
x: "-=180px",
ease: Power0.easeNone,
repeat: -1,
onRepeat: () => {
this.rearrange();
} }));
}
/**
* removes the first stick and adds one the the end
* this gives the sticks an infinite movement
*/
rearrange() {
let scale = this.speedUp();
this.timeline.timeScale(scale);
this.balltween.timeScale(scale);
$("#sticks .stick").first().remove();
new Stick();
}
/**
* The game speeds up based on score
* The GSAP timeScale() function is called on the timeline to speed up the game
* This calculates how much shall the game speed up
*/
speedUp() {
if (this.score > 30) {
return 1.8;
}
if (this.score > 20) {
return 1.7;
}
if (this.score > 15) {
return 1.5;
} else if (this.score > 12) {
return 1.4;
} else if (this.score > 10) {
return 1.3;
} else if (this.score > 8) {
return 1.2;
} else if (this.score > 5) {
return 1.1;
}
return 1;
}
/**
* Ball bouncing animation
* It checks if the ball and stick colors match
* And changes the ball color
*/
bounce() {
this.balltween.
to("#ball", this.time / 2, {
y: "+=250px",
scaleY: 0.7,
transformOrigin: "bottom",
ease: Power2.easeIn,
onComplete: () => {
this.checkColor();
} }).
to("#ball", this.time / 2, {
y: "-=250px",
scaleY: 1.1,
transformOrigin: "bottom",
ease: Power2.easeOut,
onStart: () => {
while (this.prevColor == this.color) {if (window.CP.shouldStopExecution(2)) break;
this.color = new Color().getRandomColor();
}window.CP.exitedLoop(2);
this.prevColor = this.color;
TweenMax.to("#ball", 0.5, { backgroundColor: this.color });
$("#ball").
removeClass("red").
removeClass("yellow").
removeClass("purple").
addClass(new Color().colorcodeToName(this.color));
} });
}
checkColor() {
let ballPos = $("#ball").offset().left + $("#ball").width() / 2;
let stickWidth = $(".stick").width();
let score = this.score;
$("#sticks .stick").each(function () {
if (
$(this).offset().left < ballPos &&
$(this).offset().left > ballPos - stickWidth)
{
if (
Color.getColorFromClass($(this)) == Color.getColorFromClass("#ball"))
{
// if matches increase the score
score++;
$("#score").text(score);
TweenMax.fromTo(
"#score",
0.5,
{ scale: 1.5 },
{ scale: 1, ease: Elastic.easeOut.config(1.5, 0.5) });
} else {
// you loose
game.stop();
}
}
});
this.score = score;
}}
class Stick {
constructor() {
this.stick = this.addStick();
}
addStick() {
this.stick = $("#sticks").append('<div class="stick inactive"></div>');
return this.stick;
}}
class Color {
constructor() {
this.colors = ["#FF4571", "#FFD145", "#8260F6"];
this.effects = ["bubble", "triangle", "block"];
this.prevEffect = null;
}
getRandomColor() {
let colorIndex = Math.random() * 3;
let color = this.colors[Math.floor(colorIndex)];
return color;
}
colorcodeToName(color) {
let colors = ["#FF4571", "#FFD145", "#8260F6"];
let names = ["red", "yellow", "purple"];
let index = colors.indexOf(color);
if (index == -1) return false;
return names[index];
}
/**
* Changes the color of an element
* As we as adds verbal name of the color
*/
changeColor(el) {
let index = el.data("index");
if (index === undefined) {
index = 0;
} else {
index += 1;
}
if (index == 3) index = 0;
el.css("background-color", this.colors[index]).data("index", index);
el.removeClass("red").
removeClass("yellow").
removeClass("purple").
addClass(this.colorcodeToName(this.colors[index]));
if (el.hasClass("inactive")) {
this.setEffect(el);
el.addClass("no-effect");
}
el.removeClass("inactive");
}
getRandomEffect() {
let effectIndex = null;
effectIndex = Math.floor(Math.random() * 3);
while (effectIndex == this.prevEffect) {if (window.CP.shouldStopExecution(3)) break;
effectIndex = Math.floor(Math.random() * 3);
}window.CP.exitedLoop(3);
this.prevEffect = effectIndex;
return this.effects[effectIndex];
}
/**
* Adds the effect specific particles to the stick
*/
setEffect(el) {
let effect = this.getRandomEffect();
el.addClass(effect + "-stick");
for (let i = 1; i <= 14; i++) {if (window.CP.shouldStopExecution(4)) break;
if (effect == "block") {
el.append(
`<div class="${effect} ${effect}-${i}"><div class="inner"></div><div class="inner inner-2"></div></div>`);
} else {
el.append(`<div class="${effect} ${effect}-${i}"></div>`);
}
}window.CP.exitedLoop(4);
}
/**
* Since the ball and sticks have several classes
* This method searches for the color class
* @param el [DOM element]
* @return {string} class name
*/
static getColorFromClass(el) {
let classes = $(el).attr("class").split(/\s+/);
for (var i = 0, len = classes.length; i < len; i++) {if (window.CP.shouldStopExecution(5)) break;
if (
classes[i] == "red" ||
classes[i] == "yellow" ||
classes[i] == "purple")
{
return classes[i];
}
}window.CP.exitedLoop(5);
}}
class Animation {
/**
* Creates and positions the small glow elements on the screen
*/
static generateSmallGlows(number) {
let h = $(window).height();
let w = $(window).width();
let scale = w > h ? h / 800 : w / 1200;
h = h / scale;
w = w / scale;
for (let i = 0; i < number; i++) {if (window.CP.shouldStopExecution(6)) break;
let left = Math.floor(Math.random() * w);
let top = Math.floor(Math.random() * (h / 2));
let size = Math.floor(Math.random() * 8) + 4;
$(".small-glows").prepend('<div class="small-glow"></div>');
let noise = $(".small-glows .small-glow").first();
noise.css({ left: left, top: top, height: size, width: size });
}window.CP.exitedLoop(6);
}
/**
* Creates the animations for sticks
* The effects is chosen by random
* And one of the three functions is
* Called accordingly
*/
playBubble(el) {
let bubble = new TimelineMax();
bubble.staggerFromTo(
el.find(".bubble"),
0.3,
{ scale: 0.1 },
{ scale: 1 },
0.03);
bubble.staggerTo(
el.find(".bubble"),
0.5,
{ y: "-=60px", yoyo: true, repeat: -1 },
0.03);
}
playTriangle(el) {
let triangle = new TimelineMax();
triangle.
staggerFromTo(
el.find(".triangle"),
0.3,
{ scale: 0.1 },
{ scale: 1 },
0.03).
staggerTo(
el.find(".triangle"),
1.5,
{
cycle: {
rotationY: [0, 360],
rotationX: [360, 0] },
repeat: -1,
repeatDelay: 0.1 },
0.1);
}
playBlock(el) {
let block = new TimelineMax();
let block2 = new TimelineMax({ delay: 0.69 });
block.
staggerFromTo(el.find(".block"), 0.3, { scale: 0.1 }, { scale: 1 }, 0.03).
staggerTo(
el.find(".block .inner:not(.inner-2)"),
1,
{
cycle: {
x: ["+200%", "-200%"] },
repeat: -1,
repeatDelay: 0.6 },
0.1);
block2.staggerTo(
el.find(".block .inner-2"),
1,
{
cycle: {
x: ["+200%", "-200%"] },
repeat: -1,
repeatDelay: 0.6 },
0.1);
}
static sceneAnimation() {
const speed = 15; // uses it's local speed
// animates the small glows in a circular motion
$(".small-glow").each(function () {
let speedDelta = Math.floor(Math.random() * 8);
let radius = Math.floor(Math.random() * 20) + 20;
TweenMax.to($(this), speed + speedDelta, {
rotation: 360,
transformOrigin: "-" + radius + "px -" + radius + "px",
repeat: -1,
ease: Power0.easeNone });
});
var wavet = TweenMax.to(".top_wave", speed * 1.7 / 42, {
backgroundPositionX: "-=54px",
repeat: -1,
ease: Power0.easeNone });
var wave1 = TweenMax.to(".wave1", speed * 1.9 / 42, {
backgroundPositionX: "-=54px",
repeat: -1,
ease: Power0.easeNone });
var wave2 = TweenMax.to(".wave2", speed * 2 / 42, {
backgroundPositionX: "-=54px",
repeat: -1,
ease: Power0.easeNone });
var wave3 = TweenMax.to(".wave3", speed * 2.2 / 42, {
backgroundPositionX: "-=54px",
repeat: -1,
ease: Power0.easeNone });
var wave4 = TweenMax.to(".wave4", speed * 2.4 / 42, {
backgroundPositionX: "-=54px",
repeat: -1,
ease: Power0.easeNone });
var mount1 = TweenMax.to(".mount1", speed * 8, {
backgroundPositionX: "-=1760px",
repeat: -1,
ease: Power0.easeNone });
var mount2 = TweenMax.to(".mount2", speed * 10, {
backgroundPositionX: "-=1782px",
repeat: -1,
ease: Power0.easeNone });
var clouds = TweenMax.to(".clouds", speed * 3, {
backgroundPositionX: "-=1001px",
repeat: -1,
ease: Power0.easeNone });
}}
var game = new Game();
var animation = new Animation();
var color = new Color();
var userAgent = window.navigator.userAgent;
Animation.generateSmallGlows(20);
$(document).ready(function () {
//game.showResult();
game.scaleScreen();
game.intro();
//game.start();
//game.bounce();
if ($(window).height() < 480) {
$(".play-full-page").css("display", "block");
}
});
$(document).on("click", ".stick", function () {
color.changeColor($(this));
if ($(this).hasClass("no-effect")) {
if ($(this).hasClass("bubble-stick")) {
animation.playBubble($(this));
} else if ($(this).hasClass("triangle-stick")) {
animation.playTriangle($(this));
} else if ($(this).hasClass("block-stick")) {
animation.playBlock($(this));
}
$(this).removeClass("no-effect");
}
});
$(document).on("click", ".section-2 .bar", function () {
color.changeColor($(this));
});
$(window).resize(function () {
if (!userAgent.match(/iPad/i) && !userAgent.match(/iPhone/i)) {
game.scaleScreenAndRun();
}
});
$(window).on("orientationchange", function () {
game.scaleScreenAndRun();
});
//https://codepen.io/gregh/pen/yVLOyO?editors=1010
</script>