diff --git a/examples/interactive-text.json5 b/examples/interactive-text.json5 new file mode 100644 index 00000000..f730965c --- /dev/null +++ b/examples/interactive-text.json5 @@ -0,0 +1,181 @@ +{ + width: 600, + height: 300, + outPath: "./interactiveText.mp4", + defaults: { + transition: { name: "directionalwarp" }, + layer: { + fontPath: "./assets/Patua_One/PatuaOne-Regular.ttf", + }, + }, + clips: [ + { + duration: 3, + layers: [ + { type: "rainbow-colors" }, + { + type: "slide-in-text", + text: "m", + color: "#fcba03", + position: { x: 0.04, y: 0.93, originY: "bottom", originX: "left" }, + fontSize: 0.3, + }, + { + type: "interactive-text", + text: "at", + color: "#fcba03", + position: { x: 0.3, y: 0.93, originY: "bottom", originX: "left" }, + fontSize: 0.3, + styles: [ + { + lindex: 0, + cindex: 0, + style: { + strokeWidth: 3, + stroke: "white", + fill: "red", + shadow: "0px 0px 10px rgba(0,0,0,0.3)", + }, + }, + { + lindex: 0, + cindex: 1, + style: { + strokeWidth: 3, + stroke: "white", + fill: "red", + shadow: "0px 0px 10px rgba(0,0,0,0.3)", + }, + }, + ], + }, + ], + }, + { + duration: 3, + layers: [ + { type: "rainbow-colors" }, + { + type: "slide-in-text", + text: "b", + color: "#fcba03", + position: { x: 0.04, y: 0.93, originY: "bottom", originX: "left" }, + fontSize: 0.3, + }, + { + type: "interactive-text", + text: "at", + color: "#fcba03", + position: { x: 0.23, y: 0.93, originY: "bottom", originX: "left" }, + fontSize: 0.3, + styles: [ + { + lindex: 0, + cindex: 0, + style: { + strokeWidth: 3, + stroke: "white", + fill: "red", + shadow: "0px 0px 10px rgba(0,0,0,0.3)", + }, + }, + { + lindex: 0, + cindex: 1, + style: { + strokeWidth: 3, + stroke: "white", + fill: "red", + shadow: "0px 0px 10px rgba(0,0,0,0.3)", + }, + }, + ], + }, + ], + }, + { + duration: 3, + layers: [ + { type: "rainbow-colors" }, + { + type: "slide-in-text", + text: "f", + color: "#fcba03", + position: { x: 0.04, y: 0.93, originY: "bottom", originX: "left" }, + fontSize: 0.3, + }, + { + type: "interactive-text", + text: "at", + color: "#fcba03", + position: { x: 0.23, y: 0.93, originY: "bottom", originX: "left" }, + fontSize: 0.3, + styles: [ + { + lindex: 0, + cindex: 0, + style: { + strokeWidth: 3, + stroke: "white", + fill: "red", + shadow: "0px 0px 10px rgba(0,0,0,0.3)", + }, + }, + { + lindex: 0, + cindex: 1, + style: { + strokeWidth: 3, + stroke: "white", + fill: "red", + shadow: "0px 0px 10px rgba(0,0,0,0.3)", + }, + }, + ], + }, + ], + }, + { + duration: 3, + layers: [ + { type: "rainbow-colors" }, + { + type: "slide-in-text", + text: "h", + color: "#fcba03", + position: { x: 0.04, y: 0.93, originY: "bottom", originX: "left" }, + fontSize: 0.3, + }, + { + type: "interactive-text", + text: "at", + color: "#fcba03", + position: { x: 0.23, y: 0.93, originY: "bottom", originX: "left" }, + fontSize: 0.3, + styles: [ + { + lindex: 0, + cindex: 0, + style: { + strokeWidth: 3, + stroke: "white", + fill: "red", + shadow: "0px 0px 10px rgba(0,0,0,0.3)", + }, + }, + { + lindex: 0, + cindex: 1, + style: { + strokeWidth: 3, + stroke: "white", + fill: "red", + shadow: "0px 0px 10px rgba(0,0,0,0.3)", + }, + }, + ], + }, + ], + }, + ], +} diff --git a/parseConfig.js b/parseConfig.js index eb8a4fbf..beb0d381 100644 --- a/parseConfig.js +++ b/parseConfig.js @@ -86,7 +86,7 @@ async function parseConfig({ defaults: defaultsIn = {}, clips, arbitraryAudio: a return outLayers; } - if (['title', 'subtitle', 'news-title', 'slide-in-text'].includes(type)) { + if (['title', 'subtitle', 'news-title', 'slide-in-text','interactive-text'].includes(type)) { assert(layer.text, 'Please specify a text'); let { fontFamily } = layer; diff --git a/sources/fabric/fabricFrameSources.js b/sources/fabric/fabricFrameSources.js index 14aec433..40c8e224 100644 --- a/sources/fabric/fabricFrameSources.js +++ b/sources/fabric/fabricFrameSources.js @@ -397,6 +397,38 @@ async function slideInTextFrameSource({ width, height, params: { position, text, return { onRender }; } +async function interactiveTextFrameSource({ width, height, params: { position, text, fontSize, charSpacing = 0.1, color , fontFamily = defaultFontFamily,styles } = {} }) { + async function onRender(progress, canvas) { + const fontSizeAbs = Math.round(width * fontSize || 0.05); + let stylesMap = new Map(); + if (styles) { + styles.forEach((item) => { + LayerStyleItem = stylesMap[item.lindex] || new Map(); + LayerStyleItem[item.cindex] = item.style; + stylesMap[item.lindex] = LayerStyleItem + }); + } + const { left, top, originX, originY } = getPositionProps({ position, width, height }); + const textBox = new fabric.IText(text, { + fill: color|| '#ffffff', + fontFamily, + fontSize: fontSizeAbs, + charSpacing: width * charSpacing, + styles: stylesMap + }); + textBox.setOptions({ + originX, + originY, + top, + left + + }); + canvas.add(textBox); + } + return { onRender }; +} + + async function customFabricFrameSource({ canvas, width, height, params }) { return params.func(({ width, height, fabric, canvas, params })); } @@ -412,4 +444,5 @@ module.exports = { imageFrameSource, imageOverlayFrameSource, slideInTextFrameSource, + interactiveTextFrameSource }; diff --git a/sources/frameSource.js b/sources/frameSource.js index a1aed213..2a79813d 100644 --- a/sources/frameSource.js +++ b/sources/frameSource.js @@ -3,7 +3,7 @@ const pMap = require('p-map'); const { rgbaToFabricImage, createCustomCanvasFrameSource, createFabricFrameSource, createFabricCanvas, renderFabricCanvas } = require('./fabric'); -const { customFabricFrameSource, subtitleFrameSource, titleFrameSource, newsTitleFrameSource, fillColorFrameSource, radialGradientFrameSource, linearGradientFrameSource, imageFrameSource, imageOverlayFrameSource, slideInTextFrameSource } = require('./fabric/fabricFrameSources'); +const { customFabricFrameSource, subtitleFrameSource, titleFrameSource, newsTitleFrameSource,fillColorFrameSource, radialGradientFrameSource, linearGradientFrameSource, imageFrameSource, imageOverlayFrameSource, slideInTextFrameSource, interactiveTextFrameSource } = require('./fabric/fabricFrameSources'); const createVideoFrameSource = require('./videoFrameSource'); const { createGlFrameSource } = require('./glFrameSource'); @@ -19,6 +19,7 @@ const fabricFrameSources = { 'fill-color': fillColorFrameSource, 'news-title': newsTitleFrameSource, 'slide-in-text': slideInTextFrameSource, + 'interactive-text': interactiveTextFrameSource, }; async function createFrameSource({ clip, clipIndex, width, height, channels, verbose, logTimes, ffmpegPath, ffprobePath, enableFfmpegLog, framerateStr }) {