├── styles
├── system-fonts.css
├── flexbox-center-container.css
└── style.css
├── .prettierrc.js
├── index.html
├── readme.md
└── scripts
├── confetti.min.js
└── index.js
/styles/system-fonts.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system,
3 | BlinkMacSystemFont,
4 | "Segoe UI",
5 | Roboto,
6 | Oxygen-Sans,
7 | Ubuntu,
8 | Cantarell,
9 | "Helvetica Neue",
10 | sans-serif;
11 | }
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | tabWidth: 2,
3 | printWidth: 80,
4 | endOfLine: 'lf',
5 | arrowParens: 'avoid',
6 | trailingComma: 'none',
7 | semi: true,
8 | useTabs: false,
9 | singleQuote: true,
10 | bracketSpacing: true
11 | };
12 |
--------------------------------------------------------------------------------
/styles/flexbox-center-container.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: 100%;
3 | margin: 0;
4 | }
5 |
6 | body {
7 | -webkit-box-align: center;
8 | -ms-flex-align: center;
9 | align-items: center;
10 | display: -webkit-box;
11 | display: -ms-flexbox;
12 | display: flex;
13 | }
14 |
15 | .container {
16 | margin: 0 auto;
17 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
27 |
28 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | #### birthday counter
2 |
3 | A really simple and elegant looking birthday counter
4 |
5 |
6 |
7 | #### demo (youtube)
8 |
9 | [](https://youtu.be/B-f1bxYaayc?t=53 'Birthday Countdown + Sweet Birthday Greeting with HTML, CSS & JS')
10 |
11 |
12 |
13 | #### installation
14 |
15 | ```sh
16 | git clone https://github.com/pavanjadhaw/birthday-counter
17 | ```
18 |
19 |
20 |
21 | #### usage
22 |
23 | Edit config object in `scripts/index.js`
24 | replace name and birthdate
25 |
26 | ```js
27 | 6 const config = {
28 | 7 birthdate: 'Jan 29, 2020',
29 | 8 name: 'Darlene'
30 | 9 };
31 | ```
32 |
33 |
34 |
35 | #### deploying
36 |
37 | You can deploy it to many free hosting sites
38 |
39 | Deploying to [now.sh](https://zeit.co/home)
40 |
41 | ```sh
42 | $ cd birthday-counter
43 | $ now
44 | ```
45 |
46 |
47 |
48 | Deploying to [surge.sh](https://surge.sh/)
49 |
50 | ```sh
51 | $ cd birthday-counter
52 | $ surge
53 | ```
54 |
55 |
56 |
57 | #### acknowledgements
58 |
59 | Feel free to use any part of this! Contributions are welcome,
60 | I managed to put this together year ago when I didnt know any js or anything about programming/webdev in general.
61 | So thanks to all copepen which I copied, use few parts of.
62 |
63 |
64 |
--------------------------------------------------------------------------------
/scripts/confetti.min.js:
--------------------------------------------------------------------------------
1 | window.ConfettiGenerator=function(e){function t(e,t){e||(e=1);var i=Math.random()*e;return t?Math.floor(i):i}function i(){return{prop:a.props[t(a.props.length,!0)],x:t(a.width),y:t(a.height),radius:t(4)+1,line:Math.floor(t(65)-30),angles:[t(10,!0)+2,t(10,!0)+2,t(10,!0)+2,t(10,!0)+2],color:a.colors[t(a.colors.length,!0)],rotation:t(360,!0)*Math.PI/180,speed:t(a.clock/7)+a.clock/30}}function r(e){var t=e.radius<=3?.4:.8;switch(n.fillStyle=n.strokeStyle="rgba("+e.color+", "+t+")",n.beginPath(),e.prop){case"circle":n.moveTo(e.x,e.y),n.arc(e.x,e.y,e.radius*a.size,0,2*Math.PI,!0),n.fill();break;case"triangle":n.moveTo(e.x,e.y),n.lineTo(e.x+e.angles[0]*a.size,e.y+e.angles[1]*a.size),n.lineTo(e.x+e.angles[2]*a.size,e.y+e.angles[3]*a.size),n.closePath(),n.fill();break;case"line":n.moveTo(e.x,e.y),n.lineTo(e.x+e.line*a.size,e.y+5*e.radius),n.lineWidth=2*a.size,n.stroke();break;case"square":n.save(),n.translate(e.x+15,e.y+5),n.rotate(e.rotation),n.fillRect(-15*a.size,-5*a.size,15*a.size,5*a.size),n.restore()}}var a={target:"confetti-holder",max:80,size:1,animate:!0,props:["circle","square","triangle","line"],colors:[[165,104,246],[230,61,135],[0,199,228],[253,214,126]],clock:25,interval:null,width:window.innerWidth,height:window.innerHeight};e&&(e.target&&(a.target=e.target),e.max&&(a.max=e.max),e.size&&(a.size=e.size),void 0!==e.animate&&null!==e.animate&&(a.animate=e.animate),e.props&&(a.props=e.props),e.colors&&(a.colors=e.colors),e.clock&&(a.clock=e.clock),e.width&&(a.width=e.width),e.height&&(a.height=e.height));var o=document.getElementById(a.target),n=o.getContext("2d"),l=[];return{render:function(){function e(){n.clearRect(0,0,a.width,a.height);for(var e in l)r(l[e]);s()}function s(){for(var e=0;e
a.height&&(l[e]=i,l[e].x=t(a.width,!0),l[e].y=-10)}}o.width=a.width,o.height=a.height,l=[];for(var c=0;c div {
53 | background: #F64747;
54 | position: absolute;
55 | box-shadow: 5px 10px 18px rgba(0, 0, 0, 0.4);
56 | }
57 | .giftbox > div:after, .giftbox > div:before {
58 | position: absolute;
59 | content: "";
60 | top: 0;
61 | }
62 | .giftbox:after {
63 | position: absolute;
64 | color: #fff;
65 | width: 100%;
66 | content: "Click Me!";
67 | left: 0;
68 | bottom: 0;
69 | font-size: 24px;
70 | text-align: center;
71 | transform: rotate(-20deg);
72 | transform-origin: 0 0;
73 | }
74 | .giftbox .cover {
75 | width: 100%;
76 | top: 0;
77 | left: 0;
78 | height: 25%;
79 | z-index: 2;
80 | }
81 | .giftbox .cover:before {
82 | position: absolute;
83 | height: 100%;
84 | left: 50%;
85 | width: 50px;
86 | transform: translateX(-50%);
87 | background: #2C3E50;
88 | }
89 | .giftbox .cover > div {
90 | position: absolute;
91 | width: 50px;
92 | height: 50px;
93 | left: 50%;
94 | top: -50px;
95 | transform: translateX(-50%);
96 | }
97 | .giftbox .cover > div:before, .giftbox .cover > div:after {
98 | position: absolute;
99 | left: 0;
100 | top: 0;
101 | width: 100%;
102 | height: 100%;
103 | content: "";
104 | box-shadow: inset 0 0 0 15px #2C3E50;
105 | border-radius: 30px;
106 | transform-origin: 50% 100%;
107 | }
108 | .giftbox .cover > div:before {
109 | transform: translateX(-45%) skewY(40deg);
110 | }
111 | .giftbox .cover > div:after {
112 | transform: translateX(45%) skewY(-40deg);
113 | }
114 | .giftbox .box {
115 | right: 5%;
116 | left: 5%;
117 | height: 80%;
118 | bottom: 0;
119 | }
120 | .giftbox .box:before {
121 | width: 50px;
122 | height: 100%;
123 | left: 50%;
124 | transform: translateX(-50%);
125 | background: #2C3E50;
126 | }
127 | .giftbox .box:after {
128 | width: 100%;
129 | height: 30px;
130 | background: rgba(0, 0, 0, 0);
131 | }
132 | .step-1 .giftbox {
133 | animation: wobble 0.5s linear infinite forwards;
134 | }
135 | .step-1 .cover {
136 | animation: wobble 0.5s linear infinite 5s forwards;
137 | }
138 | .step-1 .icons .row span {
139 | opacity: 1;
140 | }
141 | .step-2 .giftbox:after {
142 | opacity: 0;
143 | }
144 | .step-3 .giftbox, .step-4 .giftbox {
145 | opacity: 0;
146 | z-index: 1;
147 | }
148 | .step-3 .giftbox:after, .step-4 .giftbox:after {
149 | opacity: 0;
150 | }
151 | .step-2 .giftbox .cover {
152 | animation: flyUp 2s ease-in forwards;
153 | }
154 | .step-2 .giftbox .box {
155 | animation: flyDown 2s ease-in 0.05s forwards;
156 | }
157 | @keyframes wobble {
158 | 25% {
159 | transform: rotate(4deg);
160 | }
161 | 75% {
162 | transform: rotate(-2deg);
163 | }
164 | }
165 | @keyframes flyUp {
166 | 75% {
167 | opacity: 1;
168 | }
169 | 100% {
170 | transform: translateY(-1000px) rotate(20deg);
171 | opacity: 0;
172 | }
173 | }
174 | @keyframes flyDown {
175 | 75% {
176 | opacity: 1;
177 | }
178 | 100% {
179 | transform: translateY(100%);
180 | opacity: 0;
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/scripts/index.js:
--------------------------------------------------------------------------------
1 | const count = document.getElementById('count');
2 | const head = document.getElementById('head');
3 | const giftbox = document.getElementById('merrywrap');
4 | const canvasC = document.getElementById('c');
5 |
6 | const config = {
7 | birthdate: 'Jan 29, 2020',
8 | name: 'Darlene'
9 | };
10 |
11 | function hideEverything() {
12 | head.style.display = 'none';
13 | count.style.display = 'none';
14 | giftbox.style.display = 'none';
15 | canvasC.style.display = 'none';
16 | }
17 |
18 | hideEverything();
19 |
20 | const confettiSettings = { target: 'confetti' };
21 | const confetti = new window.ConfettiGenerator(confettiSettings);
22 | confetti.render();
23 |
24 | const second = 1000,
25 | minute = second * 60,
26 | hour = minute * 60,
27 | day = hour * 24;
28 |
29 | let countDown = new Date(`${config.birthdate} 00:00:00`).getTime();
30 | x = setInterval(function() {
31 | let now = new Date().getTime(),
32 | distance = countDown - now;
33 |
34 | document.getElementById('day').innerText = Math.floor(distance / day);
35 | document.getElementById('hour').innerText = Math.floor(
36 | (distance % day) / hour
37 | );
38 | document.getElementById('minute').innerText = Math.floor(
39 | (distance % hour) / minute
40 | );
41 | document.getElementById('second').innerText = Math.floor(
42 | (distance % minute) / second
43 | );
44 |
45 | let w = (c.width = window.innerWidth),
46 | h = (c.height = window.innerHeight),
47 | ctx = c.getContext('2d'),
48 | hw = w / 2, // half-width
49 | hh = h / 2,
50 | opts = {
51 | strings: ['HAPPY', 'BIRTHDAY!', config.name],
52 | charSize: 30,
53 | charSpacing: 35,
54 | lineHeight: 40,
55 |
56 | cx: w / 2,
57 | cy: h / 2,
58 |
59 | fireworkPrevPoints: 10,
60 | fireworkBaseLineWidth: 5,
61 | fireworkAddedLineWidth: 8,
62 | fireworkSpawnTime: 200,
63 | fireworkBaseReachTime: 30,
64 | fireworkAddedReachTime: 30,
65 | fireworkCircleBaseSize: 20,
66 | fireworkCircleAddedSize: 10,
67 | fireworkCircleBaseTime: 30,
68 | fireworkCircleAddedTime: 30,
69 | fireworkCircleFadeBaseTime: 10,
70 | fireworkCircleFadeAddedTime: 5,
71 | fireworkBaseShards: 5,
72 | fireworkAddedShards: 5,
73 | fireworkShardPrevPoints: 3,
74 | fireworkShardBaseVel: 4,
75 | fireworkShardAddedVel: 2,
76 | fireworkShardBaseSize: 3,
77 | fireworkShardAddedSize: 3,
78 | gravity: 0.1,
79 | upFlow: -0.1,
80 | letterContemplatingWaitTime: 360,
81 | balloonSpawnTime: 20,
82 | balloonBaseInflateTime: 10,
83 | balloonAddedInflateTime: 10,
84 | balloonBaseSize: 20,
85 | balloonAddedSize: 20,
86 | balloonBaseVel: 0.4,
87 | balloonAddedVel: 0.4,
88 | balloonBaseRadian: -(Math.PI / 2 - 0.5),
89 | balloonAddedRadian: -1
90 | },
91 | calc = {
92 | totalWidth:
93 | opts.charSpacing *
94 | Math.max(opts.strings[0].length, opts.strings[1].length)
95 | },
96 | Tau = Math.PI * 2,
97 | TauQuarter = Tau / 4,
98 | letters = [];
99 |
100 | ctx.font = opts.charSize + 'px Verdana';
101 |
102 | function Letter(char, x, y) {
103 | this.char = char;
104 | this.x = x;
105 | this.y = y;
106 |
107 | this.dx = -ctx.measureText(char).width / 2;
108 | this.dy = +opts.charSize / 2;
109 |
110 | this.fireworkDy = this.y - hh;
111 |
112 | let hue = (x / calc.totalWidth) * 360;
113 |
114 | this.color = 'hsl(hue,80%,50%)'.replace('hue', hue);
115 | this.lightAlphaColor = 'hsla(hue,80%,light%,alp)'.replace('hue', hue);
116 | this.lightColor = 'hsl(hue,80%,light%)'.replace('hue', hue);
117 | this.alphaColor = 'hsla(hue,80%,50%,alp)'.replace('hue', hue);
118 |
119 | this.reset();
120 | }
121 | Letter.prototype.reset = function() {
122 | this.phase = 'firework';
123 | this.tick = 0;
124 | this.spawned = false;
125 | this.spawningTime = (opts.fireworkSpawnTime * Math.random()) | 0;
126 | this.reachTime =
127 | (opts.fireworkBaseReachTime +
128 | opts.fireworkAddedReachTime * Math.random()) |
129 | 0;
130 | this.lineWidth =
131 | opts.fireworkBaseLineWidth + opts.fireworkAddedLineWidth * Math.random();
132 | this.prevPoints = [[0, hh, 0]];
133 | };
134 | Letter.prototype.step = function() {
135 | if (this.phase === 'firework') {
136 | if (!this.spawned) {
137 | ++this.tick;
138 | if (this.tick >= this.spawningTime) {
139 | this.tick = 0;
140 | this.spawned = true;
141 | }
142 | } else {
143 | ++this.tick;
144 |
145 | let linearProportion = this.tick / this.reachTime,
146 | armonicProportion = Math.sin(linearProportion * TauQuarter),
147 | x = linearProportion * this.x,
148 | y = hh + armonicProportion * this.fireworkDy;
149 |
150 | if (this.prevPoints.length > opts.fireworkPrevPoints)
151 | this.prevPoints.shift();
152 |
153 | this.prevPoints.push([x, y, linearProportion * this.lineWidth]);
154 |
155 | let lineWidthProportion = 1 / (this.prevPoints.length - 1);
156 |
157 | for (let i = 1; i < this.prevPoints.length; ++i) {
158 | let point = this.prevPoints[i],
159 | point2 = this.prevPoints[i - 1];
160 |
161 | ctx.strokeStyle = this.alphaColor.replace(
162 | 'alp',
163 | i / this.prevPoints.length
164 | );
165 | ctx.lineWidth = point[2] * lineWidthProportion * i;
166 | ctx.beginPath();
167 | ctx.moveTo(point[0], point[1]);
168 | ctx.lineTo(point2[0], point2[1]);
169 | ctx.stroke();
170 | }
171 |
172 | if (this.tick >= this.reachTime) {
173 | this.phase = 'contemplate';
174 |
175 | this.circleFinalSize =
176 | opts.fireworkCircleBaseSize +
177 | opts.fireworkCircleAddedSize * Math.random();
178 | this.circleCompleteTime =
179 | (opts.fireworkCircleBaseTime +
180 | opts.fireworkCircleAddedTime * Math.random()) |
181 | 0;
182 | this.circleCreating = true;
183 | this.circleFading = false;
184 |
185 | this.circleFadeTime =
186 | (opts.fireworkCircleFadeBaseTime +
187 | opts.fireworkCircleFadeAddedTime * Math.random()) |
188 | 0;
189 | this.tick = 0;
190 | this.tick2 = 0;
191 |
192 | this.shards = [];
193 |
194 | let shardCount =
195 | (opts.fireworkBaseShards +
196 | opts.fireworkAddedShards * Math.random()) |
197 | 0,
198 | angle = Tau / shardCount,
199 | cos = Math.cos(angle),
200 | sin = Math.sin(angle),
201 | x = 1,
202 | y = 0;
203 |
204 | for (let i = 0; i < shardCount; ++i) {
205 | let x1 = x;
206 | x = x * cos - y * sin;
207 | y = y * cos + x1 * sin;
208 |
209 | this.shards.push(new Shard(this.x, this.y, x, y, this.alphaColor));
210 | }
211 | }
212 | }
213 | } else if (this.phase === 'contemplate') {
214 | ++this.tick;
215 |
216 | if (this.circleCreating) {
217 | ++this.tick2;
218 | let proportion = this.tick2 / this.circleCompleteTime,
219 | armonic = -Math.cos(proportion * Math.PI) / 2 + 0.5;
220 |
221 | ctx.beginPath();
222 | ctx.fillStyle = this.lightAlphaColor
223 | .replace('light', 50 + 50 * proportion)
224 | .replace('alp', proportion);
225 | ctx.beginPath();
226 | ctx.arc(this.x, this.y, armonic * this.circleFinalSize, 0, Tau);
227 | ctx.fill();
228 |
229 | if (this.tick2 > this.circleCompleteTime) {
230 | this.tick2 = 0;
231 | this.circleCreating = false;
232 | this.circleFading = true;
233 | }
234 | } else if (this.circleFading) {
235 | ctx.fillStyle = this.lightColor.replace('light', 70);
236 | ctx.fillText(this.char, this.x + this.dx, this.y + this.dy);
237 |
238 | ++this.tick2;
239 | let proportion = this.tick2 / this.circleFadeTime,
240 | armonic = -Math.cos(proportion * Math.PI) / 2 + 0.5;
241 |
242 | ctx.beginPath();
243 | ctx.fillStyle = this.lightAlphaColor
244 | .replace('light', 100)
245 | .replace('alp', 1 - armonic);
246 | ctx.arc(this.x, this.y, this.circleFinalSize, 0, Tau);
247 | ctx.fill();
248 |
249 | if (this.tick2 >= this.circleFadeTime) this.circleFading = false;
250 | } else {
251 | ctx.fillStyle = this.lightColor.replace('light', 70);
252 | ctx.fillText(this.char, this.x + this.dx, this.y + this.dy);
253 | }
254 |
255 | for (let i = 0; i < this.shards.length; ++i) {
256 | this.shards[i].step();
257 |
258 | if (!this.shards[i].alive) {
259 | this.shards.splice(i, 1);
260 | --i;
261 | }
262 | }
263 |
264 | if (this.tick > opts.letterContemplatingWaitTime) {
265 | this.phase = 'balloon';
266 |
267 | this.tick = 0;
268 | this.spawning = true;
269 | this.spawnTime = (opts.balloonSpawnTime * Math.random()) | 0;
270 | this.inflating = false;
271 | this.inflateTime =
272 | (opts.balloonBaseInflateTime +
273 | opts.balloonAddedInflateTime * Math.random()) |
274 | 0;
275 | this.size =
276 | (opts.balloonBaseSize + opts.balloonAddedSize * Math.random()) | 0;
277 |
278 | let rad =
279 | opts.balloonBaseRadian + opts.balloonAddedRadian * Math.random(),
280 | vel = opts.balloonBaseVel + opts.balloonAddedVel * Math.random();
281 |
282 | this.vx = Math.cos(rad) * vel;
283 | this.vy = Math.sin(rad) * vel;
284 | }
285 | } else if (this.phase === 'balloon') {
286 | ctx.strokeStyle = this.lightColor.replace('light', 80);
287 |
288 | if (this.spawning) {
289 | ++this.tick;
290 | ctx.fillStyle = this.lightColor.replace('light', 70);
291 | ctx.fillText(this.char, this.x + this.dx, this.y + this.dy);
292 |
293 | if (this.tick >= this.spawnTime) {
294 | this.tick = 0;
295 | this.spawning = false;
296 | this.inflating = true;
297 | }
298 | } else if (this.inflating) {
299 | ++this.tick;
300 |
301 | let proportion = this.tick / this.inflateTime,
302 | x = (this.cx = this.x),
303 | y = (this.cy = this.y - this.size * proportion);
304 |
305 | ctx.fillStyle = this.alphaColor.replace('alp', proportion);
306 | ctx.beginPath();
307 | generateBalloonPath(x, y, this.size * proportion);
308 | ctx.fill();
309 |
310 | ctx.beginPath();
311 | ctx.moveTo(x, y);
312 | ctx.lineTo(x, this.y);
313 | ctx.stroke();
314 |
315 | ctx.fillStyle = this.lightColor.replace('light', 70);
316 | ctx.fillText(this.char, this.x + this.dx, this.y + this.dy);
317 |
318 | if (this.tick >= this.inflateTime) {
319 | this.tick = 0;
320 | this.inflating = false;
321 | }
322 | } else {
323 | this.cx += this.vx;
324 | this.cy += this.vy += opts.upFlow;
325 |
326 | ctx.fillStyle = this.color;
327 | ctx.beginPath();
328 | generateBalloonPath(this.cx, this.cy, this.size);
329 | ctx.fill();
330 |
331 | ctx.beginPath();
332 | ctx.moveTo(this.cx, this.cy);
333 | ctx.lineTo(this.cx, this.cy + this.size);
334 | ctx.stroke();
335 |
336 | ctx.fillStyle = this.lightColor.replace('light', 70);
337 | ctx.fillText(
338 | this.char,
339 | this.cx + this.dx,
340 | this.cy + this.dy + this.size
341 | );
342 |
343 | if (this.cy + this.size < -hh || this.cx < -hw || this.cy > hw)
344 | this.phase = 'done';
345 | }
346 | }
347 | };
348 |
349 | function Shard(x, y, vx, vy, color) {
350 | let vel =
351 | opts.fireworkShardBaseVel + opts.fireworkShardAddedVel * Math.random();
352 |
353 | this.vx = vx * vel;
354 | this.vy = vy * vel;
355 |
356 | this.x = x;
357 | this.y = y;
358 |
359 | this.prevPoints = [[x, y]];
360 | this.color = color;
361 |
362 | this.alive = true;
363 |
364 | this.size =
365 | opts.fireworkShardBaseSize + opts.fireworkShardAddedSize * Math.random();
366 | }
367 | Shard.prototype.step = function() {
368 | this.x += this.vx;
369 | this.y += this.vy += opts.gravity;
370 |
371 | if (this.prevPoints.length > opts.fireworkShardPrevPoints)
372 | this.prevPoints.shift();
373 |
374 | this.prevPoints.push([this.x, this.y]);
375 |
376 | let lineWidthProportion = this.size / this.prevPoints.length;
377 |
378 | for (let k = 0; k < this.prevPoints.length - 1; ++k) {
379 | let point = this.prevPoints[k],
380 | point2 = this.prevPoints[k + 1];
381 |
382 | ctx.strokeStyle = this.color.replace('alp', k / this.prevPoints.length);
383 | ctx.lineWidth = k * lineWidthProportion;
384 | ctx.beginPath();
385 | ctx.moveTo(point[0], point[1]);
386 | ctx.lineTo(point2[0], point2[1]);
387 | ctx.stroke();
388 | }
389 |
390 | if (this.prevPoints[0][1] > hh) this.alive = false;
391 | };
392 |
393 | function generateBalloonPath(x, y, size) {
394 | ctx.moveTo(x, y);
395 | ctx.bezierCurveTo(
396 | x - size / 2,
397 | y - size / 2,
398 | x - size / 4,
399 | y - size,
400 | x,
401 | y - size
402 | );
403 | ctx.bezierCurveTo(x + size / 4, y - size, x + size / 2, y - size / 2, x, y);
404 | }
405 |
406 | function anim() {
407 | window.requestAnimationFrame(anim);
408 |
409 | ctx.fillStyle = '#fff';
410 | ctx.fillRect(0, 0, w, h);
411 |
412 | ctx.translate(hw, hh);
413 |
414 | let done = true;
415 | for (let l = 0; l < letters.length; ++l) {
416 | letters[l].step();
417 | if (letters[l].phase !== 'done') done = false;
418 | }
419 |
420 | ctx.translate(-hw, -hh);
421 |
422 | if (done) for (let l = 0; l < letters.length; ++l) letters[l].reset();
423 | }
424 |
425 | for (let i = 0; i < opts.strings.length; ++i) {
426 | for (let j = 0; j < opts.strings[i].length; ++j) {
427 | letters.push(
428 | new Letter(
429 | opts.strings[i][j],
430 | j * opts.charSpacing +
431 | opts.charSpacing / 2 -
432 | (opts.strings[i].length * opts.charSize) / 2,
433 | i * opts.lineHeight +
434 | opts.lineHeight / 2 -
435 | (opts.strings.length * opts.lineHeight) / 2
436 | )
437 | );
438 | }
439 | }
440 |
441 | window.addEventListener('resize', function() {
442 | w = c.width = window.innerWidth;
443 | h = c.height = window.innerHeight;
444 |
445 | hw = w / 2;
446 | hh = h / 2;
447 |
448 | ctx.font = opts.charSize + 'px Verdana';
449 | });
450 |
451 | if (distance > 0) {
452 | head.style.display = 'initial';
453 | count.style.display = 'initial';
454 | } else {
455 | head.style.display = 'none';
456 | count.style.display = 'none';
457 | giftbox.style.display = 'initial';
458 | clearInterval(x);
459 | let merrywrap = document.getElementById('merrywrap');
460 | let box = merrywrap.getElementsByClassName('giftbox')[0];
461 | let step = 1;
462 | let stepMinutes = [2000, 2000, 1000, 1000];
463 |
464 | function init() {
465 | box.addEventListener('click', openBox, false);
466 | box.addEventListener('click', showfireworks, false);
467 | }
468 |
469 | function stepClass(step) {
470 | merrywrap.className = 'merrywrap';
471 | merrywrap.className = 'merrywrap step-' + step;
472 | }
473 |
474 | function openBox() {
475 | if (step === 1) {
476 | box.removeEventListener('click', openBox, false);
477 | }
478 | stepClass(step);
479 | if (step === 3) {
480 | }
481 | if (step === 4) {
482 | return;
483 | }
484 | setTimeout(openBox, stepMinutes[step - 1]);
485 | step++;
486 | // setTimeout(anim, 1900);
487 | }
488 |
489 | function showfireworks() {
490 | canvasC.style.display = 'initial';
491 | setTimeout(anim, 1500);
492 | }
493 |
494 | init();
495 | }
496 |
497 | // if (distance < 0) {
498 | // clearInterval(x);
499 | // console.log("happy birthday");
500 | // }
501 | }, second);
502 |
--------------------------------------------------------------------------------