scrawl-canvas
Version:
Version 8.9.4 - 19 Nov 2022
300 lines (226 loc) • 8.9 kB
JavaScript
// # Demo Canvas 010
// Use video sources and media streams for Picture entitys
// [Run code](../../demo/canvas-010.html)
import {
addListener,
importDomVideo,
importMediaStream,
library as L,
makePicture,
makeRender,
observeAndUpdate,
} from '../source/scrawl.js'
import { reportSpeed } from './utilities.js';
// #### Scene setup
let canvas = L.artefact.mycanvas;
// ##### Importing video sources
// __Get video stream from DOM <video> element__
// + When loading video assets from the DOM, note that Scrawl-canvas has to deal with the <video> element operating under normal DOM rules. This means that (in most modern browsers) the video will not fetch anything beyond its metadata until at least 1px height of the video is displayed in the viewport (videos hidden by any CSS rules will not fetch anything until they are made visible).
// + The practical implications of this is that any Picture entitys relying on the video as their asset will not display an image until the DOM video element appears in the user's viewport.
// + To make sure the Picture entity displays the video's first frame, we need to explicitly set the DOM element's preload attribute to __auto__
importDomVideo('.myvideo');
// __Create Picture entity__ from video entity included in the DOM
let viddyOne = makePicture({
name: 'first-video',
asset: 'waves',
width: 200,
height: 200,
startX: 300,
startY: 200,
handleX: 100,
handleY: 100,
copyWidth: 200,
copyHeight: 200,
copyStartX: 100,
copyStartY: 100,
lineWidth: 10,
strokeStyle: 'lightgreen',
order: 1,
method: 'drawThenFill',
});
// __Import a video from a remote server__
let viddyTwo = makePicture({
name: 'second-video',
videoSource: 'img/Motion - 18249.mp4',
width: '100%',
height: '100%',
copyWidth: '100%',
copyHeight: '100%',
order: 0,
method: 'fill',
});
// __Display a device-based media stream__ in a Picture entity
// + Note 1: Users will need to explicitly agree to let Scrawl-canvas use the media stream the first time the page loads (the browser should handle this agreement procedure itself)
// + Note 2: importMediaStream returns a Promise!
let viddyThree;
importMediaStream({
audio: false,
})
.then(myface => {
viddyThree = makePicture({
name: 'mediastream-video',
asset: myface.name,
startX: '20%',
startY: '20%',
handleX: 'center',
handleY: 'center',
width: '40%',
height: '40%',
copyWidth: '100%',
copyHeight: '100%',
lineWidth: 6,
strokeStyle: 'pink',
order: 0,
method: 'drawThenFill',
});
// Adding some controls to manipulate the media stream's display
// + For this demo, we'll use the existing controls setup for manipulating the DOM video
observeAndUpdate({
event: ['input', 'change'],
origin: '.controlItem',
target: viddyThree,
useNativeListener: true,
preventDefault: true,
updates: {
copy_start_xPercent: ['copyStartX', '%'],
copy_start_xAbsolute: ['copyStartX', 'round'],
copy_start_yPercent: ['copyStartY', '%'],
copy_start_yAbsolute: ['copyStartY', 'round'],
copy_dims_widthPercent: ['copyWidth', '%'],
copy_dims_widthAbsolute: ['copyWidth', 'round'],
copy_dims_heightPercent: ['copyHeight', '%'],
copy_dims_heightAbsolute: ['copyHeight', 'round'],
upend: ['flipUpend', 'boolean'],
reverse: ['flipReverse', 'boolean'],
},
});
})
.catch(err => console.log(err.message));
// #### Scene animation
// Function to display frames-per-second data, and other information relevant to the demo
const report = reportSpeed('#reportmessage', function () {
let [copyX, copyY] = viddyOne.copyStart;
let [copyW, copyH] = viddyOne.copyDimensions;
let [pasteX, pasteY] = viddyOne.start;
let [pasteW, pasteH] = viddyOne.dimensions;
let [handleX, handleY] = viddyOne.handle;
let {roll, scale} = viddyOne;
return ` Copy - x: ${copyX}, y: ${copyY}, w: ${copyW}, h: ${copyH}
Paste - x: ${pasteX}, y: ${pasteY}, w: ${pasteW}, h:${pasteH}
Handle - x: ${handleX}, y: ${handleY}
Roll: ${roll}; Scale: ${scale}`;
});
// Create the Display cycle animation
makeRender({
name: 'demo-animation',
target: canvas,
afterShow: report,
});
// #### User interaction
// Setup form observer functionality
observeAndUpdate({
event: ['input', 'change'],
origin: '.controlItem',
target: viddyOne,
useNativeListener: true,
preventDefault: true,
updates: {
copy_start_xPercent: ['copyStartX', '%'],
copy_start_xAbsolute: ['copyStartX', 'round'],
copy_start_yPercent: ['copyStartY', '%'],
copy_start_yAbsolute: ['copyStartY', 'round'],
copy_dims_widthPercent: ['copyWidth', '%'],
copy_dims_widthAbsolute: ['copyWidth', 'round'],
copy_dims_heightPercent: ['copyHeight', '%'],
copy_dims_heightAbsolute: ['copyHeight', 'round'],
paste_dims_widthPercent: ['width', '%'],
paste_dims_widthAbsolute: ['width', 'round'],
paste_dims_heightPercent: ['height', '%'],
paste_dims_heightAbsolute: ['height', 'round'],
paste_start_xPercent: ['startX', '%'],
paste_start_xAbsolute: ['startX', 'round'],
paste_start_xString: ['startX', 'raw'],
paste_start_yPercent: ['startY', '%'],
paste_start_yAbsolute: ['startY', 'round'],
paste_start_yString: ['startY', 'raw'],
paste_handle_xPercent: ['handleX', '%'],
paste_handle_xAbsolute: ['handleX', 'round'],
paste_handle_xString: ['handleX', 'raw'],
paste_handle_yPercent: ['handleY', '%'],
paste_handle_yAbsolute: ['handleY', 'round'],
paste_handle_yString: ['handleY', 'raw'],
roll: ['roll', 'float'],
scale: ['scale', 'float'],
upend: ['flipUpend', 'boolean'],
reverse: ['flipReverse', 'boolean'],
},
});
// Add an additional click event listener
// + Because many browsers/devices will not allow video to be played until a user interacts with it in some way
addListener('up', function () {
viddyOne.set({
video_muted: true,
video_loop: true,
}).videoPlay();
viddyTwo.set({
video_muted: true,
video_loop: true,
}).videoPlay();
}, canvas.domElement);
// Setup form
// @ts-expect-error
document.querySelector('#copy_start_xPercent').value = 25;
// @ts-expect-error
document.querySelector('#copy_start_yPercent').value = 25;
// @ts-expect-error
document.querySelector('#copy_dims_widthPercent').value = 50;
// @ts-expect-error
document.querySelector('#copy_dims_widthAbsolute').value = 200;
// @ts-expect-error
document.querySelector('#copy_start_xAbsolute').value = 100;
// @ts-expect-error
document.querySelector('#copy_start_yAbsolute').value = 100;
// @ts-expect-error
document.querySelector('#copy_dims_heightPercent').value = 50;
// @ts-expect-error
document.querySelector('#copy_dims_heightAbsolute').value = 200;
// @ts-expect-error
document.querySelector('#paste_dims_widthPercent').value = 33;
// @ts-expect-error
document.querySelector('#paste_dims_widthAbsolute').value = 200;
// @ts-expect-error
document.querySelector('#paste_dims_heightPercent').value = 50;
// @ts-expect-error
document.querySelector('#paste_dims_heightAbsolute').value = 200;
// @ts-expect-error
document.querySelector('#paste_start_xPercent').value = 50;
// @ts-expect-error
document.querySelector('#paste_start_yPercent').value = 50;
// @ts-expect-error
document.querySelector('#paste_handle_xPercent').value = 50;
// @ts-expect-error
document.querySelector('#paste_handle_yPercent').value = 50;
// @ts-expect-error
document.querySelector('#paste_start_xAbsolute').value = 300;
// @ts-expect-error
document.querySelector('#paste_start_yAbsolute').value = 200;
// @ts-expect-error
document.querySelector('#paste_handle_xAbsolute').value = 100;
// @ts-expect-error
document.querySelector('#paste_handle_yAbsolute').value = 100;
// @ts-expect-error
document.querySelector('#paste_start_xString').options.selectedIndex = 1;
// @ts-expect-error
document.querySelector('#paste_start_yString').options.selectedIndex = 1;
// @ts-expect-error
document.querySelector('#paste_handle_xString').options.selectedIndex = 1;
// @ts-expect-error
document.querySelector('#paste_handle_yString').options.selectedIndex = 1;
// @ts-expect-error
document.querySelector('#roll').value = 0;
// @ts-expect-error
document.querySelector('#scale').value = 1;
// @ts-expect-error
document.querySelector('#upend').options.selectedIndex = 0;
// @ts-expect-error
document.querySelector('#reverse').options.selectedIndex = 0;