You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pdd-order-api/bak/canvas_video.js

564 lines
21 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

let createCanvas = null;
let loadImage = null;
let createImageData = null;
const isWeb = typeof window != 'undefined';
if (isWeb) {
loadImage = function(imageUrl) {
return new Promise(function (resolve, reject) {
var img = new Image;
img.setAttribute('crossOrigin', 'anonymous');
img.src = imageUrl;
img.onload = function () {
resolve(img);
}
});
}
} else {
const canvas = require('canvas');
createCanvas = canvas.createCanvas;
loadImage = canvas.loadImage;
createImageData = canvas.createImageData;
}
Date.prototype.Format = function(fmt) { //author: meizz
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
class ImageObject {
constructor(imageIndex, imageUrl, thumbWidth, thumbHeight, displayEndTime) {
this.imageIndex = imageIndex;
this.imageUrl = imageUrl;
this.imageData = null;
this.isAnimationEnd = false;
this.displayEndTime = displayEndTime;
this.nowStep = 1;
this.width = thumbWidth;
this.height = thumbHeight;
this.canvas = null;
this.ctx = null;
}
async load() {
this.image = await loadImage(this.imageUrl);
if (typeof document != 'undefined') {
if ($('J_thumbImageCanvas_' + this.imageIndex).length == 0) {
$('#J_cerateVideoPlayer').append('<canvas id="J_thumbImageCanvas_'+this.imageIndex+'" style="display: none"></canvas>')
}
this.canvas = document.getElementById('J_thumbImageCanvas_' + this.imageIndex);
this.canvas.width = this.width;
this.canvas.height = this.height;
} else {
this.canvas = createCanvas(this.width, this.height);
}
this.ctx = this.canvas.getContext('2d');
let x = (this.canvas.width - this.image.width)/2;
let y = (this.canvas.height - this.image.height)/2;
if (x < y && x < 0) {
let scale = this.canvas.width / this.image.width;
this.ctx.scale(scale, scale);
x = y = 0;
} else if (y <= x && y < 0) {
let scale = this.canvas.height / this.image.height;
this.ctx.scale(scale, scale);
x = y = 0;
} else {
this.ctx.scale(1, 1);
}
this.ctx.fillStyle = 'white';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.drawImage(this.image, x, y, this.image.width, this.image.height);
this.imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
//console.log('load ', this.imageUrl,this.width, this.height, this.image.width, this.image.height);
}
}
class CanvasVideo {
constructor(imageUrlList, videoEffect, musicCode, videoSeconds, width, height, saveFilename, progressCallback) {
//canvas
this.progressCallbackTimer = null;
this.progressCallback = progressCallback;
this.videoPlayStatus = 'wait';
this.isWeb = typeof window != 'undefined';
this.width = width;
this.height = height;
this.fps = 30;
this.saveFilename = saveFilename;
if (this.isWeb) {
this.canvas = document.getElementById('J_videoCanvas');
} else {
this.canvas = createCanvas(this.width, this.height);
}
this.ctx = this.canvas.getContext('2d');
this.ctx.clearRect(0,0, this.canvas.width, this.canvas.height);
//video
this.videoEffect = videoEffect;
this.musicCode = musicCode;
this.imageTime = Math.floor(videoSeconds * 1000 / imageUrlList.length);
this.videoTotalTime = videoSeconds * 1000;
this.currentTime = 0;
this.imageObjectList = [];
for(let i in imageUrlList) {
let displayEndTime = Math.floor(this.imageTime * (parseInt(i)+1));
this.imageObjectList.push(new ImageObject(i, imageUrlList[i], width, height, displayEndTime));
}
this.initEncoder();
}
initEncoder() {
}
debug() {
//console.log(...arguments);
}
renderSceneFade() {
let imageIndex = this.getCurrentImageIndex();
let imageObj = this.imageObjectList[imageIndex];
let lastImageObj = imageIndex > 0 ? this.imageObjectList[imageIndex-1] : false;
if (imageObj.isAnimationEnd) {
this.debug('isAnimationEnd skip ', imageIndex);
return;
}
if (!lastImageObj) {
this.debug('first .. ', this.currentTime, imageObj.displayEndTime);
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.globalAlpha = 1;
if (this.isWeb) {
this.ctx.putImageData(imageObj.imageData, 0, 0);
} else {
this.encoder.setDelay(imageObj.displayEndTime - this.currentTime);
this.encoder.addFrame(imageObj.imageData.data);
}
imageObj.isAnimationEnd = true;
return;
}
var step = 20;
let nowStep = imageObj.nowStep;
var endImageData = imageObj.imageData
var startImageData = lastImageObj.imageData;
if (this.isWeb) {
var imageData = this.ctx.createImageData(imageObj.width, imageObj.height);
} else {
var imageData = createImageData(imageObj.width, imageObj.height);
}
for (var x = 1; x <= imageObj.width; x+=1) {
for (var y = 1; y <= imageObj.height; y+= 1) {
var index = 4 * ((y - 1) * imageObj.width + (x - 1));
// 变为绿色色值依次是0, 128, 0, 256
// imagedata.data[index] = 0;
// imagedata.data[index + 1] = 128;
// imagedata.data[index + 2] = 0;
// imagedata.data[index + 3] = 256;
for( var i=0;i<3;i++) {
imageData.data[index + i] = (endImageData.data[index + i] - startImageData.data[index + i]) / step * nowStep +startImageData.data[index + i];
}
imageData.data[index + 3] = 256;
}
}
imageObj.isAnimationEnd = step==nowStep;
imageObj.nowStep++;
this.debug(imageObj.displayEndTime, imageObj.isAnimationEnd,imageObj.nowStep,imageObj.width,imageObj.height )
if (this.isWeb) {
this.ctx.putImageData(imageData, 0, 0);
} else {
if (imageObj.isAnimationEnd) {
this.encoder.setDelay(imageObj.displayEndTime - this.currentTime);
} else {
this.encoder.setFrameRate(this.fps);
}
this.encoder.addFrame(imageData.data);
}
}
renderSceneZoom() {
let imageIndex = this.getCurrentImageIndex();
let imageObj = this.imageObjectList[imageIndex];
if (typeof imageObj.scale == 'undefined') {
imageObj.scale = 0.8;
}
if (typeof imageObj.alpha == 'undefined') {
imageObj.alpha = 0.5;
}
if (imageObj.isAnimationEnd) {
this.debug('isAnimationEnd skip ', imageIndex);
return;
}
// this.ctx.globalAlpha = 1;
// this.ctx.fillStyle = 'white';
// this.ctx.fillRect(0, 0, this.width, this.height);
let frameCount = Math.ceil(500/1000*this.fps);
imageObj.scale += 0.4/frameCount;
imageObj.scale = Math.min(1.2, imageObj.scale);
imageObj.alpha += 0.5/frameCount;
imageObj.alpha = Math.min(1, imageObj.alpha);
imageObj.isAnimationEnd = imageObj.scale==1.2;
if (imageObj.isAnimationEnd) {
imageObj.alpha = 1;
!this.isWeb && this.encoder.setDelay(imageObj.displayEndTime - this.currentTime);
} else {
!this.isWeb && this.encoder.setFrameRate(this.fps);
}
let newX = (imageObj.width * imageObj.scale - imageObj.width)/-2;
let newY = (imageObj.height * imageObj.scale - imageObj.height)/-2;
this.ctx.globalAlpha = imageObj.alpha;
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.drawImage(imageObj.canvas, newX, newY, imageObj.width * imageObj.scale, imageObj.height * imageObj.scale);
this.debug('add frame ', imageObj.scale, imageObj.alpha);
!this.isWeb && this.encoder.addFrame(this.ctx);
}
renderSceneSlide() {
let imageIndex = this.getCurrentImageIndex();
let imageObj = this.imageObjectList[imageIndex];
let direction = imageIndex % 2 == 0 ? 'left': 'right';
let moveDistance = Math.ceil(imageObj.width / 2);
let duration = 500;
if (typeof imageObj.moveX == 'undefined') {
imageObj.moveX = direction == 'left' ? -moveDistance : moveDistance;
}
if (typeof imageObj.alpha == 'undefined') {
imageObj.alpha = 0;
}
if (imageObj.isAnimationEnd) {
this.debug('isAnimationEnd skip ', imageIndex);
return;
}
if (imageIndex == 0) {
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.globalAlpha = 1;
this.ctx.fillStyle = 'white';
this.ctx.fillRect(0, 0, this.width, this.height);
}
let frameCount = Math.ceil(duration/1000*this.fps);
let moveStep = Math.ceil(moveDistance / frameCount) * (direction == 'left' ? 1 : -1);
imageObj.moveX += moveStep;
if (direction == 'left') {
imageObj.moveX = Math.min(0, imageObj.moveX);
} else {
imageObj.moveX = Math.max(0, imageObj.moveX);
}
imageObj.alpha += 1/frameCount;
imageObj.alpha = Math.min(1, imageObj.alpha);
imageObj.isAnimationEnd = imageObj.moveX==0;
if (imageObj.isAnimationEnd) {
imageObj.alpha = 1;
!this.isWeb && this.encoder.setDelay(imageObj.displayEndTime - this.currentTime);
} else {
!this.isWeb && this.encoder.setFrameRate(this.fps);
}
this.ctx.globalAlpha = imageObj.alpha;
this.ctx.drawImage(imageObj.canvas, imageObj.moveX, 0, imageObj.width, imageObj.height);
this.debug('add frame ', imageObj.moveX, imageObj.alpha);
!this.isWeb && this.encoder.addFrame(this.ctx);
}
renderShutters() {
let imageIndex = this.getCurrentImageIndex();
let imageObj = this.imageObjectList[imageIndex];
let lastImageObj = imageIndex > 0 ? this.imageObjectList[imageIndex-1] : false;
if (imageObj.isAnimationEnd) {
this.debug('isAnimationEnd skip ', imageIndex);
return;
}
if (!lastImageObj) {
this.debug('first .. ', this.currentTime, imageObj.displayEndTime);
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.globalAlpha = 1;
if (this.isWeb) {
this.ctx.putImageData(imageObj.imageData, 0, 0);
} else {
this.encoder.setDelay(imageObj.displayEndTime - this.currentTime);
this.encoder.addFrame(imageObj.imageData.data);
}
imageObj.isAnimationEnd = true;
return;
}
var direction = imageIndex % 4;
var step = 900 / 1000 * this.fps;
var lineWidth = 40;
var lineDistance = 30;
let nowStep = imageObj.nowStep;
var endImageData = imageObj.imageData
var startImageData = lastImageObj.imageData;
if (this.isWeb) {
var imageData = this.ctx.createImageData(imageObj.width, imageObj.height);
} else {
var imageData = createImageData(imageObj.width, imageObj.height);
}
for (var x = 1; x <= imageObj.width; x+=1) {
for (var y = 1; y <= imageObj.height; y+= 1) {
var index = 4 * ((y - 1) * imageObj.width + (x - 1));
if (direction == 0) {
let lineTotal = Math.ceil(imageObj.width / lineWidth);
let lineIndex = Math.ceil(x /lineWidth);
let oldPixMaxY = imageObj.height - imageObj.height / step * nowStep;
oldPixMaxY = oldPixMaxY - (lineTotal - lineIndex) * lineWidth / 3 * 2;
for (var i = 0; i < 4; i++) {
imageData.data[index + i] = y < oldPixMaxY ? startImageData.data[index + i] : endImageData.data[index + i];
}
imageData.data[index + 3] = 256;
} else if (direction == 1) {
let lineTotal = Math.ceil(imageObj.height / lineWidth);
let lineIndex = Math.ceil(y /lineWidth);
let oldPixMinX = imageObj.width / step * nowStep;
oldPixMinX = oldPixMinX + (lineTotal - lineIndex) * lineWidth / 3 * 2;
for (var i = 0; i < 4; i++) {
imageData.data[index + i] = x > oldPixMinX ? startImageData.data[index + i] : endImageData.data[index + i];
}
imageData.data[index + 3] = 256;
} else if (direction == 2) {
let lineTotal = Math.ceil(imageObj.width / lineWidth);
let lineIndex = Math.ceil(x /lineWidth);
let oldPixMinY = imageObj.height / step * nowStep;
oldPixMinY = oldPixMinY + (lineTotal - lineIndex) * lineWidth / 3 * 2;
for (var i = 0; i < 4; i++) {
imageData.data[index + i] = y > oldPixMinY ? startImageData.data[index + i] : endImageData.data[index + i];
}
imageData.data[index + 3] = 256;
} else {
let lineTotal = Math.ceil(imageObj.height / lineWidth);
let lineIndex = Math.ceil(y /lineWidth);
let oldPixMaxX = imageObj.width - imageObj.width / step * nowStep;
oldPixMaxX = oldPixMaxX - (lineTotal - lineIndex) * lineWidth / 3 * 2;
for (var i = 0; i < 4; i++) {
imageData.data[index + i] = x < oldPixMaxX ? startImageData.data[index + i] : endImageData.data[index + i];
}
imageData.data[index + 3] = 256;
}
}
}
imageObj.isAnimationEnd = step==nowStep;
imageObj.nowStep++;
this.debug(imageObj.displayEndTime, imageObj.isAnimationEnd,imageObj.nowStep,imageObj.width,imageObj.height )
if (this.isWeb) {
this.ctx.putImageData(imageData, 0, 0);
} else {
if (imageObj.isAnimationEnd) {
this.encoder.setDelay(imageObj.displayEndTime - this.currentTime);
} else {
this.encoder.setFrameRate(this.fps);
}
this.encoder.addFrame(imageData.data);
}
}
renderRotate() {
let imageIndex = this.getCurrentImageIndex();
let imageObj = this.imageObjectList[imageIndex];
let duration = 300;
let rotateAngle = 45;
if (typeof imageObj.rotate == 'undefined') {
imageObj.rotate = -45;
}
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
if (imageObj.isAnimationEnd) {
this.debug('isAnimationEnd skip ', imageIndex);
return;
}
if (imageIndex == 0) {
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.globalAlpha = 1;
this.ctx.fillStyle = 'white';
this.ctx.fillRect(0, 0, this.width, this.height);
}
let frameCount = Math.ceil(duration / 1000 * this.fps);
let moveStep = Math.ceil(rotateAngle / frameCount);
imageObj.rotate += moveStep;
imageObj.rotate = Math.min(0, imageObj.rotate);
imageObj.isAnimationEnd = imageObj.rotate==0;
if (imageObj.isAnimationEnd) {
!this.isWeb && this.encoder.setDelay(imageObj.displayEndTime - this.currentTime);
} else {
!this.isWeb && this.encoder.setFrameRate(this.fps);
}
this.ctx.rotate(imageObj.rotate * Math.PI / 180);
this.ctx.drawImage(imageObj.canvas, 0, 0, imageObj.width, imageObj.height);
this.debug('add frame ', imageObj.moveX, imageObj.alpha);
!this.isWeb && this.encoder.addFrame(this.ctx);
}
renderSceneSkew() {
let imageIndex = this.getCurrentImageIndex();
let imageObj = this.imageObjectList[imageIndex];
let duration = 400;
let angle = 1;
if (typeof imageObj.angle == 'undefined') {
imageObj.angle = 1;
}
if (imageObj.isAnimationEnd) {
this.debug('isAnimationEnd skip ', imageIndex);
return;
}
let frameCount = Math.ceil(duration / 1000 * this.fps);
let moveStep = angle / frameCount;
imageObj.angle -= moveStep;
imageObj.angle = Math.max(0, imageObj.angle);
imageObj.isAnimationEnd = imageObj.angle==0;
if (imageObj.isAnimationEnd) {
!this.isWeb && this.encoder.setDelay(imageObj.displayEndTime - this.currentTime);
} else {
!this.isWeb && this.encoder.setFrameRate(this.fps);
}
this.ctx.setTransform(1, 0, imageObj.angle, 1, 0, 0);
this.ctx.drawImage(imageObj.canvas, 0, 0, imageObj.width, imageObj.height);
this.debug('add frame ', imageObj.moveX, imageObj.alpha);
//fs.writeFileSync('img/'+idx+'.png', this.canvas.toBuffer());
!this.isWeb && this.encoder.addFrame(this.ctx);
}
getCurrentImageIndex() {
return Math.floor(this.currentTime/this.imageTime);
}
pause() {
this.videoPlayStatus = 'pause';
}
stop() {
this.videoPlayStatus = 'stop';
this.currentTime = 0;
}
play(oneFrame) {
let startTs = (new Date()).getTime();
let timeStep = Math.ceil(1000/this.fps);
let self = this;
if (this.isWeb) {
self.videoPlayStatus = 'playing';
function render() {
if (typeof self.progressCallback == 'function') {
self.progressCallback({
currentImageIndex: self.getCurrentImageIndex(),
videoTotalTime: self.videoTotalTime,
currentTime: self.currentTime,
videoPlayStatus: self.videoPlayStatus,
});
}
if (self.videoPlayStatus == 'pause') {
self.progressCallbackTimer && clearTimeout(self.progressCallbackTimer);
return;
}
if (self.videoPlayStatus == 'stop') {
self.progressCallbackTimer && clearTimeout(self.progressCallbackTimer);
self.currentTime = 0;
return;
}
if (self.videoPlayStatus == 'playing') {
self.render();
self.currentTime += timeStep;
if (oneFrame) {
self.videoPlayStatus = 'pause';
}
if (self.currentTime >= self.videoTotalTime) {
self.videoPlayStatus = 'wait';
self.currentTime = 0;
}
self.progressCallbackTimer = setTimeout(render, timeStep);
}
}
render();
} else {
while(this.currentTime < this.videoTotalTime) {
this.render();
this.currentTime += timeStep;
}
this.encoder.finish();
}
let endTs = (new Date()).getTime();
this.makeGifSeconds = Math.ceil((endTs - startTs)/1000);
}
async loadImages() {
let startTs = (new Date()).getTime();
for (let i = 0; i< this.imageObjectList.length;i++) {
await this.imageObjectList[i].load();
}
this.loadImageSeconds = Math.ceil(((new Date()).getTime() - startTs)/1000);
}
render() {
switch (this.videoEffect) {
case 'sceneFade'://淡入当初
this.renderSceneFade();
break;
case 'sceneZoom'://图片放大
this.renderSceneZoom();
break;
case 'sceneSlide'://左右幻灯
this.renderSceneSlide();
break;
case 'sceneShutters'://百叶窗
this.renderShutters();
break;
case 'sceneRotate'://旋转飞入
this.renderRotate();
break;
case 'sceneSkew': //斜角切换
this.renderSceneSkew()
break;
}
}
}
if (typeof module !== 'undefined') {
module.exports = CanvasVideo;
}