Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const path = require('path');

const tmpDir = __dirname + '/tmp/';
const publicDir = __dirname + '/public/';
const imagesDir = __dirname + '/images/';
const defaultBgImage = 'bg-image.jpg';

// canvas generator
const CountdownGenerator = require('./countdown-generator');
Expand All @@ -21,33 +23,39 @@ app.get('/', function (req, res) {

// generate and download the gif
app.get('/generate', function (req, res) {
let {time, width, height, color, bg, name, frames} = req.query;
let {time, width, height, color, bg, name, withImageBg, frames} = req.query;

if(!time){
throw Error('Time parameter is required.');
}

CountdownGenerator.init(time, width, height, color, bg, name, frames, () => {
const imageBgSrc = `${imagesDir}${defaultBgImage}`;


CountdownGenerator.init(time, width, height, color, bg, name, (withImageBg ? imageBgSrc : null), frames, () => {
let filePath = tmpDir + name + '.gif';
res.download(filePath);
res.sendFile(filePath);
});
});

// serve the gif to a browser
app.get('/serve', function (req, res) {
let {time, width, height, color, bg, name, frames} = req.query;
let {time, width, height, color, bg, name, withImageBg, frames} = req.query;

if(!time){
throw Error('Time parameter is required.');
}

CountdownGenerator.init(time, width, height, color, bg, name, frames, () => {
const imageBgSrc = `${imagesDir}${defaultBgImage}`;


CountdownGenerator.init(time, width, height, color, bg, name, (withImageBg ? imageBgSrc : null), frames, () => {
let filePath = tmpDir + name + '.gif';
res.sendFile(filePath);
});
});

app.listen(process.env.PORT || 3000, function(){
app.listen(process.env.PORT || 3500, function(){
console.log("Express server listening on port %d in %s mode", this.address().port, app.settings.env);
});

Expand Down
80 changes: 48 additions & 32 deletions countdown-generator/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,40 @@ module.exports = {
* @param {number} frames
* @param {requestCallback} cb - The callback that is run once complete.
*/
init: function(time, width=200, height=200, color='ffffff', bg='000000', name='default', frames=30, cb){
init: function (time, width = 200, height = 200, color = 'ffffff', bg = '000000', name = 'default', imageBgSrc, frames = 30, cb) {
// Set some sensible upper / lower bounds
this.width = this.clamp(width, 150, 500);
this.height = this.clamp(height, 150, 500);
this.frames = this.clamp(frames, 1, 90);

this.bg = '#' + bg;
this.textColor = '#' + color;
this.name = name;

// loop optimisations
this.halfWidth = Number(this.width / 2);
this.halfHeight = Number(this.height / 2);

this.encoder = new GIFEncoder(this.width, this.height);
this.canvas = new Canvas(this.width, this.height);
this.ctx = this.canvas.getContext('2d');


var $this = this;

// calculate the time difference (if any)
let timeResult = this.time(time);

// start the gif encoder
this.encode(timeResult, cb);
if (imageBgSrc !== null) {
fs.readFile(imageBgSrc, function (err, image) {
if (err) throw err;
var img = new Canvas.Image;
img.src = image;
$this.encode(timeResult, img, cb);
});
} else {
this.encode(timeResult, null, cb);
}
},
/**
* Limit a value between a min / max
Expand All @@ -50,7 +61,7 @@ module.exports = {
* @param max - maximum value number can have
* @returns {number}
*/
clamp: function(number, min, max){
clamp: function (number, min, max) {
return Math.max(min, Math.min(number, max));
},
/**
Expand All @@ -62,12 +73,12 @@ module.exports = {
// grab the current and target time
let target = moment(timeString);
let current = moment();

// difference between the 2 (in ms)
let difference = target.diff(current);

// either the date has passed, or we have a difference
if(difference <= 0){
if (difference <= 0) {
return 'Date has passed!';
} else {
// duration of the difference
Expand All @@ -79,89 +90,94 @@ module.exports = {
* @param {string|Object} timeResult - either the date passed string, or a valid moment duration object
* @param {requestCallback} cb - the callback to be run once complete
*/
encode: function(timeResult, cb){
encode: function (timeResult, image, cb) {
let enc = this.encoder;
let ctx = this.ctx;
let tmpDir = process.cwd() + '/tmp/';

// create the tmp directory if it doesn't exist
if (!fs.existsSync(tmpDir)){
if (!fs.existsSync(tmpDir)) {
fs.mkdirSync(tmpDir);
}

let filePath = tmpDir + this.name + '.gif';

// pipe the image to the filesystem to be written
let imageStream = enc
.createReadStream()
.pipe(fs.createWriteStream(filePath));
.createReadStream()
.pipe(fs.createWriteStream(filePath));
// once finised, generate or serve
imageStream.on('finish', () => {
// only execute callback if it is a function
typeof cb === 'function' && cb();
});

// estimate the font size based on the provided width
let fontSize = Math.floor(this.width / 12) + 'px';
let fontFamily = 'Courier New'; // monospace works slightly better
let fontFamily = 'Roboto'; // monospace works slightly better

// set the font style
ctx.font = [fontSize, fontFamily].join(' ');
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';


// start encoding gif with following settings
enc.start();
enc.setRepeat(0);
enc.setDelay(1000);
enc.setQuality(10);

// if we have a moment duration object
if(typeof timeResult === 'object'){
for(let i = 0; i < this.frames; i++){
if (typeof timeResult === 'object') {
for (let i = 0; i < this.frames; i++) {
// extract the information we need from the duration
let days = Math.floor(timeResult.asDays());
let hours = Math.floor(timeResult.asHours() - (days * 24));
let minutes = Math.floor(timeResult.asMinutes()) - (days * 24 * 60) - (hours * 60);
let seconds = Math.floor(timeResult.asSeconds()) - (days * 24 * 60 * 60) - (hours * 60 * 60) - (minutes * 60);

// make sure we have at least 2 characters in the string
days = (days.toString().length == 1) ? '0' + days : days;
hours = (hours.toString().length == 1) ? '0' + hours : hours;
minutes = (minutes.toString().length == 1) ? '0' + minutes : minutes;
seconds = (seconds.toString().length == 1) ? '0' + seconds : seconds;

// build the date string
let string = [days, 'd ', hours, 'h ', minutes, 'm ', seconds, 's'].join('');

// paint BG
ctx.fillStyle = this.bg;
ctx.fillRect(0, 0, this.width, this.height);

if (image !== null) {
ctx.drawImage(image, 0, 0, this.width, this.height)
}
// paint text
ctx.fillStyle = this.textColor;
ctx.fillText(string, this.halfWidth, this.halfHeight);

// add finalised frame to the gif
enc.addFrame(ctx);

// remove a second for the next loop
timeResult.subtract(1, 'seconds');
}
} else {
// Date has passed so only using a string

// BG
ctx.fillStyle = this.bg;
ctx.fillRect(0, 0, this.width, this.height);

// Text
ctx.fillStyle = this.textColor;
ctx.fillText(timeResult, this.halfWidth, this.halfHeight);
enc.addFrame(ctx);
}

// finish the gif

enc.finish();

// finish the gif

}
};
Binary file added images/bg-image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/ex1.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"author": "Matt Hobbs",
"license": "ISC",
"dependencies": {
"canvas": "^1.3.16",
"express": "^4.13.4",
"gifencoder": "^1.0.6",
"moment": "^2.13.0"
"canvas": "1.3.16",
"express": "4.13.4",
"gifencoder": "1.0.6",
"moment": "2.13.0"
},
"engines": {
"node": "6.0.0"
Expand Down
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="og:image" content="http://localhost:5000/generate?time=2018-09-24T20:35&name=ex1">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
Expand Down