timesnap-core
v0.3.3
Published
Take screenshots of web pages at smooth intervals
Downloads
23
Readme
timesnap
timesnap is a Node.js program that records screenshots of web pages that use JavaScript animations. It uses timeweb and puppeteer to open a web page, overwrite its time-handling functions, and record snapshots at virtual times. For some web pages, this allows frames to be recorded slower than real time, while appearing smooth and consistent when recreated into a video.
It requires Node v8.9.0 or higher and npm.
timesnap-core
timesnap-core is a version of timesnap that does not automatically bundle puppeteer. It differs from timesnap
by requiring a config.launcher
function or a config.browser
object to be passed, and does not have a command line interface. It's stored on the core
branch of timesnap
and derived from its code. All pull requests should be based on the main branch of timesnap
instead of this branch, unless in the rare event that it's particular only to this branch.
To record screenshots and compile them into a video, see timecut and timecut-core. For using virtual time in browser, see timeweb.
# timeweb and timesnap Limitations
timeweb (and timesnap by extension) only overwrites JavaScript functions and video playback, so pages where changes occur via other means (e.g. through transitions/animations from CSS rules) will likely not render as intended.
Read Me Contents
# From Node.js
# Node Install
npm install timesnap-core --save
# Node Examples
For these examples, we'll use puppeteer version 2.1.1, which requires fewer additional libraries to be installed.
npm install [email protected] --save
# Basic Use:
Specify a config.launcher
function that creates a browser instance with certain launch options.
const timesnap = require('timesnap-core');
const puppeteer = require('puppeteer');
timesnap({
launcher: launchOptions => puppeteer.launch(launchOptions),
url: 'https://tungs.github.io/truchet-tiles-original/#autoplay=true&switchStyle=random',
viewport: {
width: 800, // sets the viewport (window size) to 800x600
height: 600
},
selector: '#container', // crops each frame to the bounding box of '#container'
left: 20, top: 40, // further crops the left by 20px, and the top by 40px
right: 6, bottom: 30, // and the right by 6px, and the bottom by 30px
fps: 30, // saves 30 frames for each virtual second
duration: 20, // for 20 virtual seconds
outputDirectory: 'frames' // to frames/001.png... frames/600.png
// of the current working directory
}).then(function () {
console.log('Done!');
});
# Using config.browser
:
You can also use config.browser
, though it might ignore / disable some launch options like config.quiet
, config.logToStdErr
, config.headless
, config.executablePath
, and config.launchArguments
. You can specify custom launch arguments through puppeteer.launch()
.
const timesnap = require('timesnap-core');
const puppeteer = require('puppeteer');
timesnap({
browser: puppeteer.launch({ dumpio: true }), // can add custom launch options here
url: 'https://tungs.github.io/truchet-tiles-original/#autoplay=true&switchStyle=random',
outputDirectory: 'frames'
}).then(function () {
console.log('Done!');
});
# Node API
timesnap(config)
- #
config
<Object>- #
launcher
<function(Object)> A function that returns or resolves a puppeteer or puppeteer-like browser. It is passed a launch options argument, which should be passed topuppeteer.launch
, if possible. - #
browser
<Object> The instance of a puppeteer or puppeteer-like browser. Note that certain configuration options might not work as intended or might be ignored, likeconfig.quiet
,config.logToStdErr
,config.headless
,config.executablePath
, andconfig.launchArguments
. - #
url
<string> The url to load. It can be a web url, likehttps://github.com
or a file path, with relative paths resolving in the current working directory (default:index.html
). - #
outputDirectory
<string> Saves images to a directory. Makes one if necessary. - #
outputPattern
<string> Sets each file name according to a printf-style pattern (e.g.image-%03d.png
) - #
fps
<number> Frame rate, in frames per virtual second, of capture (default:60
). - #
duration
<number> Duration of capture, in seconds (default:5
). - #
frames
<number> Number of frames to capture. Overrides default fps or default duration. - #
selector
<string> Crops each frame to the bounding box of the first item found by the specified CSS selector. - #
viewport
<Object>- #
width
<number> Width of viewport, in pixels (default:800
). - #
height
<number> Height of viewport, in pixels (default:600
). - #
deviceScaleFactor
<number> Device scale factor (default:1
). Note that the captured image resolution is multiplied by the device scale factor. - #
isMobile
<boolean> Specifies whether themeta viewport
tag should be used (default:false
). - #
hasTouch
<boolean> Specifies whether the viewport supports touch (default:false
). - #
isLandscape
<boolean> Specifies whether the viewport is in landscape mode (default:false
).
- #
- #
canvasCaptureMode
<boolean | string>- Experimental. Captures images from canvas data instead of screenshots. See canvas capture mode. Can provide an optional image format (e.g.
png
), otherwise it uses the saved image's extension, or defaults topng
if the format is not specified or supported. Can prefix the format withimmediate:
(e.g.immediate:png
) to immediately capture pixel data after rendering, which is sometimes needed for some WebGL renderers. Specify the canvas by settingconfig.selector
, otherwise it defaults to the first canvas in the document.
- Experimental. Captures images from canvas data instead of screenshots. See canvas capture mode. Can provide an optional image format (e.g.
- #
start
<number> Runs code forconfig.start
virtual seconds before saving any frames (default:0
). - #
xOffset
<number> X offset of capture, in pixels (default:0
). - #
yOffset
<number> Y offset of capture, in pixels (default:0
). - #
width
<number> Width of capture, in pixels. - #
height
<number> Height of capture, in pixels. - #
transparentBackground
<boolean> Allows background to be transparent if there is no background styling. - #
roundToEvenWidth
<boolean> Rounds capture width up to the nearest even number. - #
roundToEvenHeight
<boolean> Rounds capture height up to the nearest even number. - #
left
<number> Left edge of capture, in pixels. Equivalent toconfig.xOffset
. - #
right
<number> Right edge of capture, in pixels. Ignored ifconfig.width
is specified. - #
top
<number> Top edge of capture, in pixels. Equivalent toconfig.yOffset
. - #
bottom
<number> Bottom edge of capture, in pixels. Ignored ifconfig.height
is specified. - #
unrandomize
<boolean | string | number | Array<number>> OverwritesMath.random
with a seeded pseudorandom number generator. If it is a number, an array of up to four numbers, or a string of up to four comma separated numbers, then those values are used as the initial seeds. If it is true, then the default seed is used. If it is the string 'random-seed', a random seed will be generated, displayed (if quiet mode is not enabled), and used. - #
executablePath
<string> Uses the Chromium/Chrome instance atconfig.executablePath
for puppeteer. - #
remoteUrl
<string> URL of remote Chromium/Chrome instance to connect usingpuppeteer.connect()
. - #
launchArguments
<Array <string>> Extra arguments for Puppeteer/Chromium. Example:['--single-process']
. A list of arguments can be found here. - #
headless
<boolean> Runs puppeteer in headless (nonwindowed) mode (default:true
). - #
screenshotType
<string> Output image format for the screenshots. By default, the file extension is used to infer type, and failing that,'png'
is used.'jpeg'
is also available. - #
screenshotQuality
<number> Quality level between 0 to 1 for lossy screenshots. Defaults to 0.92 when in canvas capture mode and 0.8 otherwise. - #
startDelay
<number> Waitsconfig.startDelay
real seconds after loading before starting (default:0
). - #
quiet
<boolean> Suppresses console logging. - #
logger
<function(...Object)> Replaces console logging with a particular function. The passed arguments are the same as those toconsole.log
(in this case, usually one string). - #
logToStdErr
<boolean> Logs to stderr instead of stdout. Doesn't do anything ifconfig.quiet
is set to true. - #
shouldSkipFrame
<function(Object)> A function that determines whether a current frame should be skipped for capturing. It should returntrue
if the current frame should be skipped,false
if not. It is passed the following object: - #
stopFunctionName
<string> function name that the client web page can call to stop capturing. For instance,'stopCapture'
could be called in the client, viastopCapture()
. - #
frameProcessor
<function(Buffer, number, number)> A function that will be called after capturing each frame. Ifconfig.outputDirectory
andconfig.outputPattern
aren't specified, enabling this suppresses automatic file output. After capturing each frame,config.frameProcessor
is called with three arguments, and if it returns a promise, capture will be paused until the promise resolves: - #
navigatePageToURL
<function(Object)> A function that navigates a puppeteer page to a URL, overriding the default navigation to a URL. The function should return a promise that resolves once the page is finished navigating. The function is passed the following object: - #
preparePage
<function(Page)> A setup function that will be called one time before taking screenshots. If it returns a promise, capture will be paused until the promise resolves.page
<Page> The puppeteer instance of the page being captured.
- #
preparePageForScreenshot
<function(Page, number, number)> A setup function that will be called before each screenshot. If it returns a promise, capture will be paused until the promise resolves.
- #
- # returns: <Promise> resolves after all the frames have been captured.
# timesnap Modes
timesnap can capture frames using one of two modes:
- # Screenshot capture mode (default) uses puppeteer's built-in API to take screenshots of Chromium/Chrome windows. It can capture most parts of a webpage (e.g. div, svg, canvas) as they are rendered on the webpage. It can crop images, round to even widths/heights, but it usually runs slower than canvas capture mode.
- # Canvas capture mode (experimental) directly copies data from a canvas element and is often faster than using screenshot capture mode. If the background of the canvas is transparent, it may show up as transparent or black depending on the captured image format. Configuration options that adjust the crop and round to an even width/height do not currently have an effect. To use this mode, use the
--canvas-capture-mode
option from the command line or setconfig.canvasCaptureMode
from Node.js. Also specify the canvas using a css selector, using the--selector
option from the command line or settingconfig.selector
from Node.js, otherwise it uses the first canvas element.
# How it works
timesnap uses puppeteer's page.evaluateOnNewDocument
feature to automatically overwrite a page's native time-handling JavaScript functions and objects (new Date()
, Date.now
, performance.now
, requestAnimationFrame
, setTimeout
, setInterval
, cancelAnimationFrame
, cancelTimeout
, and cancelInterval
) to custom ones that use a virtual timeline, allowing for JavaScript computation to complete before taking a screenshot.
This work was inspired by a talk by Noah Veltman, who described altering a document's Date.now
and performance.now
functions to refer to a virtual time and using puppeteer
to change that virtual time and take snapshots.