提交 18013225 编写于 作者: R root

Auto Commit

上级 669eac6d
'use strict';
// server
const express = require('express');
const app = express();
const path = require('path');
const tmpDir = __dirname + '/tmp/';
const publicDir = __dirname + '/public/';
// canvas generator
const CountdownGenerator = require('./countdown-generator');
app.use(express.static(publicDir));
app.use(express.static(tmpDir));
// root
app.get('/', function(req, res) {
res.sendFile(publicDir + 'index.html');
});
// generate and download the gif
app.get('/generate', function(req, res) {
let { time, width, height, color, bg, name, frames } = req.query;
if (!time) {
throw Error('Time parameter is required.');
}
CountdownGenerator.init(time, width, height, color, bg, name, frames, () => {
let filePath = tmpDir + name + '.gif';
res.download(filePath);
});
});
// serve the gif to a browser
app.get('/serve', function(req, res) {
let { time, width, height, color, bg, name='default', frames } = req.query;
if (!time) {
throw Error('Time parameter is required.');
}
CountdownGenerator.init(time, width, height, color, bg, name, frames, () => {
let filePath = tmpDir + name + '.gif';
res.sendFile(filePath);
});
});
app.listen(process.env.PORT || 2025, function() {
console.log("Express server listening on port %d in %s mode", this.address().port, app.settings.env);
});
module.exports = app;
\ No newline at end of file
'use strict';
const fs = require('fs');
const path = require('path');
const GIFEncoder = require('gifencoder');
const Canvas = require('canvas');
const moment = require('moment');
module.exports = {
/**
* Initialise the GIF generation
* @param {string} time
* @param {number} width
* @param {number} height
* @param {string} color
* @param {string} bg
* @param {string} name
* @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) {
// 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 = Canvas.createCanvas(this.width, this.height);
this.ctx = this.canvas.getContext('2d');
// calculate the time difference (if any)
let timeResult = this.time(time);
// start the gif encoder
this.encode(timeResult, cb);
},
/**
* Limit a value between a min / max
* @link http://stackoverflow.com/questions/11409895/whats-the-most-elegant-way-to-cap-a-number-to-a-segment
* @param number - input number
* @param min - minimum value number can have
* @param max - maximum value number can have
* @returns {number}
*/
clamp: function(number, min, max) {
return Math.max(min, Math.min(number, max));
},
/**
* Calculate the diffeence between timeString and current time
* @param {string} timeString
* @returns {string|Object} - return either the date passed string, or a valid moment duration object
*/
time: function(timeString) {
// 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) {
return 'Date has passed!';
} else {
// duration of the difference
return moment.duration(difference);
}
},
/**
* Encode the GIF with the information provided by the time function
* @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) {
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)) {
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));
// 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
// 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++) {
// 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);
// 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();
}
};
\ No newline at end of file
console.log("欢迎来到 InsCode");
\ No newline at end of file
{ {
"name": "nodejs", "name": "date-gif",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "Use Node to generate an animated countdown gif.",
"main": "index.js", "main": "app.js",
"scripts": { "scripts": {
"dev": "node index.js", "test": "echo \"Error: no test specified\" && exit 1",
"test": "echo \"Error: no test specified\" && exit 1" "dev": "node app.js"
}, },
"keywords": [], "author": "yma16",
"author": "", "license": "ISC",
"license": "ISC", "dependencies": {
"dependencies": { "canvas": "^3.0.0",
"@types/node": "^18.0.6", "express": "^5.0.0",
"node-fetch": "^3.2.6" "gifencoder": "^2.0.0",
} "moment": "^2.30.0"
},
"engines": {
"node": ">=6.0.0"
} }
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Using Node.js to generate countdown gifs</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.6/lumen/bootstrap.min.css" rel="stylesheet" integrity="sha384-mvYjhBJXQ9VlNETV/xXShy849GsBHnKzVVudnMOcWUVM/6Nd2ksj8VNng5f8ylyX" crossorigin="anonymous">
<body>
<div class="container">
<div class="page-header">
<h1>Gif countdown generator <small>Using node</small></h1>
</div>
<p class="lead">The very simple app I have created allows you to generate a countdown timer animated gif depending on the URL parameters you provide. <a href="https://github.com/Nooshu/node-countdown-gif">View the code</a>.</p>
<h2>URL Parameters</h2>
<ul>
<li><strong>time<sup>*</sup></strong> - Date &amp; time when your countdown will end [e.g. 2016-06-24T20:35]</li>
<li><strong>frames</strong> - number of frames (also number of seconds) the countdown will run before looping [defaults to 30]</li>
<li><strong>width</strong> - width in pixels [defaults to 200]</li>
<li><strong>height</strong> - height in pixels [defaults to 200]</li>
<li><strong>bg</strong> - hex colour code for the background [defaults to 000000]</li>
<li><strong>color</strong> - hex colour code for the text [defaults to ffffff]</li>
<li><strong>name</strong> - filename used for the generated gif [defaults to 'default']</li>
</ul>
<p><small><sup>*</sup> required.</small></p>
<h2>Generate Examples <small>Triggers a download</small></h2>
<ul>
<li>Basic: <a href="/generate?time=2025-09-24T20:35&name=ex1">/generate?time=2025-09-24T20:35</a></li>
<li>Custom dimensions: <a href="/generate?time=2025-09-24T20:35&width=300&height=150&name=ex2">/generate?time=2025-09-24T20:35&width=300&height=150</a></li>
<li>Custom colours: <a href="/generate?time=2025-09-24T20:35&bg=028900&color=adff00&name=ex3">/generate?time=2025-09-24T20:35&bg=028900&color=adff00</a></li>
<li>Custom name &amp; frames: <a href="/generate?time=2025-09-24T20:35&name=awesome-gif&frames=20&name=ex4">/generate?time=2025-09-24T20:35&name=awesome-gif&frames=20</a></li>
</ul>
<h2>Serve Examples <small>Display in an image tag</small></h2>
<div class="row">
<div class="col-sm-6 col-md-4">
<div class="thumbnail">
<img src="/serve?time=2025-09-24T20:35&name=serve1" alt="basic example">
<div class="caption">
<h3>Basic example</h3>
<p>Simple example setting only the date + time.</p>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="thumbnail">
<img src="/serve?time=2025-09-24T20:35&bg=028900&color=adff00&name=serve2" alt="custom colours">
<div class="caption">
<h3>Custom colours</h3>
<p>Example setting a custom set of colours.</p>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="thumbnail">
<img src="/serve?time=2025-09-24T20:35&bg=028900&color=adff00&width=200&height=100&name=serve3" alt="custom colours">
<div class="caption">
<h3>Custom dimensions</h3>
<p>Example setting a custom set of dimensions.</p>
</div>
</div>
</div>
</div>
<p>Under the hood the app uses:</p>
<ul>
<li><a href="https://nodejs.org/en/blog/release/v6.0.0/">Node.js</a></li>
<li><a href="http://expressjs.com/">Express</a></li>
<li><a href="https://github.com/Automattic/node-canvas">Node-canvas</a></li>
<li><a href="https://github.com/eugeneware/gifencoder">Gifencoder</a></li>
<li><a href="http://momentjs.com/">Moment.js</a></li>
<li><a href="https://www.heroku.com/">Hosted on Heroku.com</a></li>
</ul>
</div>
</body>
</html>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册