diff --git a/README.md b/README.md
index 6cb9e24a1..9cce11a03 100644
--- a/README.md
+++ b/README.md
@@ -84,7 +84,8 @@ var art = new Artplayer({
| Project | Description | Demo |
| ----------------------------------------------------------------------------------------------------------------- | ---------------------------- | ---------------------------------------- |
-| [artplayer-plugin-danmu](https://github.com/zhw2590582/ArtPlayer/tree/master/packages/artplayer-plugin-danmu) | Danmu plugin for ArtPlayer | `WIP` |
+| [artplayer-plugin-danmu](https://github.com/zhw2590582/ArtPlayer/tree/master/packages/artplayer-plugin-danmu) | Danmu plugin for ArtPlayer | `WIP` |
+| [artplayer-plugin-gif](https://github.com/zhw2590582/ArtPlayer/tree/master/packages/artplayer-plugin-gif) | Gif plugin for ArtPlayer | [demo](https://artplayer.org/gif/) |
| [artplayer-tool-thumbnail](https://github.com/zhw2590582/ArtPlayer/tree/master/packages/artplayer-tool-thumbnail) | Thumbnail tool for ArtPlayer | [demo](https://artplayer.org/thumbnail/) |
## QQ Group
diff --git a/docs/lab/css/codemirror.css b/docs/assets/css/codemirror.css
similarity index 100%
rename from docs/lab/css/codemirror.css
rename to docs/assets/css/codemirror.css
diff --git a/docs/lab/css/consola.css b/docs/assets/css/consola.css
similarity index 100%
rename from docs/lab/css/consola.css
rename to docs/assets/css/consola.css
diff --git a/docs/lab/css/normalize.css b/docs/assets/css/normalize.css
similarity index 100%
rename from docs/lab/css/normalize.css
rename to docs/assets/css/normalize.css
diff --git a/docs/lab/img/favicon.ico b/docs/assets/img/favicon.ico
similarity index 100%
rename from docs/lab/img/favicon.ico
rename to docs/assets/img/favicon.ico
diff --git a/docs/lab/js/codemirror.js b/docs/assets/js/codemirror.js
similarity index 100%
rename from docs/lab/js/codemirror.js
rename to docs/assets/js/codemirror.js
diff --git a/docs/lab/js/consola.js b/docs/assets/js/consola.js
similarity index 100%
rename from docs/lab/js/consola.js
rename to docs/assets/js/consola.js
diff --git a/docs/lab/js/javascript.js b/docs/assets/js/javascript.js
similarity index 100%
rename from docs/lab/js/javascript.js
rename to docs/assets/js/javascript.js
diff --git a/docs/gif/css/common.css b/docs/gif/css/common.css
new file mode 100644
index 000000000..4e7c98a94
--- /dev/null
+++ b/docs/gif/css/common.css
@@ -0,0 +1,51 @@
+html, body, .container {
+ height: 100%;
+}
+
+.container {
+ display: flex;
+ flex-direction: column;
+}
+
+.part-top {
+ display: flex;
+ height: 70%;
+}
+
+.part-bottom {
+ position: relative;
+ height: 30%;
+}
+
+.part-left {
+ width: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.part-right {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.part-left .artplayer {
+ width: 500px;
+ height: 500px;
+ background: #fff;
+}
+
+.part-right .preview {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 500px;
+ height: 500px;
+ background: #f3f3f3;
+}
+
+.part-right .preview img {
+ max-width: 100%;
+}
\ No newline at end of file
diff --git a/docs/gif/index.html b/docs/gif/index.html
new file mode 100644
index 000000000..25c172ab0
--- /dev/null
+++ b/docs/gif/index.html
@@ -0,0 +1,45 @@
+
+
+
+
+ Gif plugin for ArtPlayer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/gif/js/common.js b/docs/gif/js/common.js
new file mode 100644
index 000000000..cc522d489
--- /dev/null
+++ b/docs/gif/js/common.js
@@ -0,0 +1,34 @@
+'use strict';
+
+(function() {
+ consola.creat({
+ target: '.part-bottom',
+ size: '100%',
+ zIndex: 99,
+ });
+
+ console.info('Welcome, if you like it, consider star it, thank you.');
+ console.info('https://github.com/zhw2590582/ArtPlayer');
+
+ window.addEventListener('error', function(err) {
+ console.error(err.message);
+ });
+
+ var art = new Artplayer({
+ container: '.artplayer',
+ url: 'https://blog.zhw-island.com/assets-cdn/video/one-more-time-one-more-chance-480p.mp4',
+ title: '【新海诚动画】『秒速5センチメートル』',
+ autoSize: true,
+ controls: [
+ {
+ name: 'preview',
+ position: 'right',
+ html: '打开',
+ mounted: $preview => {
+ art.plugins.localPreview.attach($preview);
+ },
+ },
+ ],
+ plugins: [artplayerPluginGif]
+ });
+})();
diff --git a/docs/lab/index.html b/docs/lab/index.html
index e2266d58e..7e09c98af 100644
--- a/docs/lab/index.html
+++ b/docs/lab/index.html
@@ -8,11 +8,11 @@
-
-
+
+
+
+
-
-
@@ -37,9 +37,9 @@
-
-
-
+
+
+
+
diff --git a/docs/thumbnail/js/common.js b/docs/thumbnail/js/common.js
index 7987b5a32..d23c7694a 100644
--- a/docs/thumbnail/js/common.js
+++ b/docs/thumbnail/js/common.js
@@ -30,7 +30,7 @@
zIndex: 99,
});
- console.info('Welcome to this tool, if you like it, consider star it, thank you.');
+ console.info('Welcome, if you like it, consider star it, thank you.');
console.info('https://github.com/zhw2590582/ArtPlayer');
window.addEventListener('error', function(err) {
diff --git a/docs/thumbnail/js/consola.js b/docs/thumbnail/js/consola.js
deleted file mode 100644
index 7fa05c34c..000000000
--- a/docs/thumbnail/js/consola.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var o in n)("object"==typeof exports?exports:e)[o]=n[o]}}(window,function(){return function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";var o=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{};if(e=o({},u,e),!document.querySelector(".consola-container"))return s=document.querySelector(e.target),i.default.insertHtml(s,"beforeend",''),f=s.querySelector(".consola-wrap"),d=s.querySelector(".consola-count span"),s.querySelector(".consola-clean").addEventListener("click",l.clean),function(e,t,n){var o=function(t){var o=e[t];e[t]=function(){o.apply(this,arguments),n(t,[].concat(Array.prototype.slice.call(arguments)))}},r=!0,i=!1,c=void 0;try{for(var l,u=t[Symbol.iterator]();!(r=(l=u.next()).done);r=!0){o(l.value)}}catch(e){i=!0,c=e}finally{try{!r&&u.return&&u.return()}finally{if(i)throw c}}}(window.console,a,function(e,t){i.default.insertHtml(f,"beforeend",'\n
\n
'+function(e){var t=document.createElement("div");e=e.map(function(e){return(0,r.default)(e)}),t.textContent=e;var n=t.innerHTML;return t=null,n}(t)+"
\n
"),d.innerHTML=f.children.length,f.scrollTo(0,f.scrollHeight)}),l},l.clean=function(){return f.innerHTML="",d.innerHTML="0",l},window.consola=l,e.exports=l},function(e,t){e.exports=function e(t){switch(typeof t){case"undefined":return"undefined";case"object":let n=Object.prototype.toString.call(t).slice(8,-1).toLowerCase();switch(n){case"null":return"null";case"array":return"["+t.map(t=>e(t)).join(", ")+"]";case"object":return"{ "+Object.keys(t).map(n=>n+": "+e(t[n])).join(", ")+" }";default:try{return t.toString()}catch(e){return"[Unknown type: "+n+"]"}}default:return t.toString()}}},function(e,t,n){},,function(e,t,n){"use strict";function o(e){for(var t=e.length,n=[],o=0;o1&&void 0!==arguments[1]?arguments[1]:document).querySelectorAll(e))},find:function(e,t){return o(e.querySelectorAll(t))},removeClass:function(e,t){e.classList.remove(t)},addClass:function(e,t){e.classList.add(t)},hasClass:function(e,t){return e.classList.contains(t)},removeElement:function(e){e&&e.parentNode&&e.parentNode.removeChild(e)},closest:function(e,t){for(var n=e.matches||e.webkitMatchesSelector||e.mozMatchesSelector||e.msMatchesSelector;e&&!n.call(e,t);)e=e.parentElement;return e},getStyle:function(e,t,n){var o=window.getComputedStyle(e,null).getPropertyValue(t);return n?Number.parseFloat(o,10):o},blur:function(){document.activeElement&&document.activeElement.blur()},insertHtml:function(e,t,n){var o=["beforebegin","afterbegin","beforeend","afterend"];if(!o.includes(t))throw new TypeError("'position' must be one of them: "+o.join("、"));e.insertAdjacentHTML(t,n)},setAttributes:function(e,t){Object.keys(t).forEach(function(n){!1!==t[n]?e.setAttribute(n,t[n]):e.removeAttribute(n)})},setStyles:function(e,t){Object.keys(t).forEach(function(n){e.style[n]=t[n]})}}}])});
\ No newline at end of file
diff --git a/docs/uncompiled/artplayer-plugin-gif.js b/docs/uncompiled/artplayer-plugin-gif.js
new file mode 100644
index 000000000..1600412c3
--- /dev/null
+++ b/docs/uncompiled/artplayer-plugin-gif.js
@@ -0,0 +1,2864 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (factory((global['artplayer-plugin-gif'] = {})));
+}(this, (function (exports) { 'use strict';
+
+ function _defineProperty(obj, key, value) {
+ if (key in obj) {
+ Object.defineProperty(obj, key, {
+ value: value,
+ enumerable: true,
+ configurable: true,
+ writable: true
+ });
+ } else {
+ obj[key] = value;
+ }
+
+ return obj;
+ }
+
+ var defineProperty = _defineProperty;
+
+ function _objectSpread(target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i] != null ? arguments[i] : {};
+ var ownKeys = Object.keys(source);
+
+ if (typeof Object.getOwnPropertySymbols === 'function') {
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
+ }));
+ }
+
+ ownKeys.forEach(function (key) {
+ defineProperty(target, key, source[key]);
+ });
+ }
+
+ return target;
+ }
+
+ var objectSpread = _objectSpread;
+
+ function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+ }
+
+ var gifshot = createCommonjsModule(function (module, exports) {
+
+ (function (window, document, navigator, undefined) {
+ /*
+ utils.js
+ ========
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+ var utils = {
+ URL: window.URL || window.webkitURL || window.mozURL || window.msURL,
+ getUserMedia: function () {
+ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
+ return getUserMedia ? getUserMedia.bind(navigator) : getUserMedia;
+ }(),
+ requestAnimFrame: window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame,
+ requestTimeout: function requestTimeout(callback, delay) {
+ callback = callback || utils.noop;
+ delay = delay || 0;
+
+ if (!utils.requestAnimFrame) {
+ return setTimeout(callback, delay);
+ }
+
+ var start = new Date().getTime();
+ var handle = new Object();
+ var requestAnimFrame = utils.requestAnimFrame;
+
+ var loop = function loop() {
+ var current = new Date().getTime();
+ var delta = current - start;
+ delta >= delay ? callback.call() : handle.value = requestAnimFrame(loop);
+ };
+
+ handle.value = requestAnimFrame(loop);
+ return handle;
+ },
+ Blob: window.Blob || window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder,
+ btoa: function () {
+ var btoa = window.btoa || function (input) {
+ var output = '';
+ var i = 0;
+ var l = input.length;
+ var key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+ var chr1 = void 0;
+ var chr2 = void 0;
+ var chr3 = void 0;
+ var enc1 = void 0;
+ var enc2 = void 0;
+ var enc3 = void 0;
+ var enc4 = void 0;
+
+ while (i < l) {
+ chr1 = input.charCodeAt(i++);
+ chr2 = input.charCodeAt(i++);
+ chr3 = input.charCodeAt(i++);
+ enc1 = chr1 >> 2;
+ enc2 = (chr1 & 3) << 4 | chr2 >> 4;
+ enc3 = (chr2 & 15) << 2 | chr3 >> 6;
+ enc4 = chr3 & 63;
+
+ if (isNaN(chr2)) {
+ enc3 = enc4 = 64;
+ } else if (isNaN(chr3)) {
+ enc4 = 64;
+ }
+
+ output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4);
+ }
+
+ return output;
+ };
+
+ return btoa ? btoa.bind(window) : utils.noop;
+ }(),
+ isObject: function isObject(obj) {
+ return obj && Object.prototype.toString.call(obj) === '[object Object]';
+ },
+ isEmptyObject: function isEmptyObject(obj) {
+ return utils.isObject(obj) && !Object.keys(obj).length;
+ },
+ isArray: function isArray(arr) {
+ return arr && Array.isArray(arr);
+ },
+ isFunction: function isFunction(func) {
+ return func && typeof func === 'function';
+ },
+ isElement: function isElement(elem) {
+ return elem && elem.nodeType === 1;
+ },
+ isString: function isString(value) {
+ return typeof value === 'string' || Object.prototype.toString.call(value) === '[object String]';
+ },
+ isSupported: {
+ canvas: function canvas() {
+ var el = document.createElement('canvas');
+ return el && el.getContext && el.getContext('2d');
+ },
+ webworkers: function webworkers() {
+ return window.Worker;
+ },
+ blob: function blob() {
+ return utils.Blob;
+ },
+ Uint8Array: function Uint8Array() {
+ return window.Uint8Array;
+ },
+ Uint32Array: function Uint32Array() {
+ return window.Uint32Array;
+ },
+ videoCodecs: function () {
+ var testEl = document.createElement('video');
+ var supportObj = {
+ 'mp4': false,
+ 'h264': false,
+ 'ogv': false,
+ 'ogg': false,
+ 'webm': false
+ };
+
+ try {
+ if (testEl && testEl.canPlayType) {
+ // Check for MPEG-4 support
+ supportObj.mp4 = testEl.canPlayType('video/mp4; codecs="mp4v.20.8"') !== ''; // Check for h264 support
+
+ supportObj.h264 = (testEl.canPlayType('video/mp4; codecs="avc1.42E01E"') || testEl.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"')) !== ''; // Check for Ogv support
+
+ supportObj.ogv = testEl.canPlayType('video/ogg; codecs="theora"') !== ''; // Check for Ogg support
+
+ supportObj.ogg = testEl.canPlayType('video/ogg; codecs="theora"') !== ''; // Check for Webm support
+
+ supportObj.webm = testEl.canPlayType('video/webm; codecs="vp8, vorbis"') !== -1;
+ }
+ } catch (e) {}
+
+ return supportObj;
+ }()
+ },
+ noop: function noop() {},
+ each: function each(collection, callback) {
+ var x = void 0;
+ var len = void 0;
+
+ if (utils.isArray(collection)) {
+ x = -1;
+ len = collection.length;
+
+ while (++x < len) {
+ if (callback(x, collection[x]) === false) {
+ break;
+ }
+ }
+ } else if (utils.isObject(collection)) {
+ for (x in collection) {
+ if (collection.hasOwnProperty(x)) {
+ if (callback(x, collection[x]) === false) {
+ break;
+ }
+ }
+ }
+ }
+ },
+ mergeOptions: function mergeOptions(defaultOptions, userOptions) {
+ if (!utils.isObject(defaultOptions) || !utils.isObject(userOptions) || !Object.keys) {
+ return;
+ }
+
+ var newObj = {};
+ utils.each(defaultOptions, function (key, val) {
+ newObj[key] = defaultOptions[key];
+ });
+ utils.each(userOptions, function (key, val) {
+ var currentUserOption = userOptions[key];
+
+ if (!utils.isObject(currentUserOption)) {
+ newObj[key] = currentUserOption;
+ } else {
+ if (!defaultOptions[key]) {
+ newObj[key] = currentUserOption;
+ } else {
+ newObj[key] = utils.mergeOptions(defaultOptions[key], currentUserOption);
+ }
+ }
+ });
+ return newObj;
+ },
+ setCSSAttr: function setCSSAttr(elem, attr, val) {
+ if (!utils.isElement(elem)) {
+ return;
+ }
+
+ if (utils.isString(attr) && utils.isString(val)) {
+ elem.style[attr] = val;
+ } else if (utils.isObject(attr)) {
+ utils.each(attr, function (key, val) {
+ elem.style[key] = val;
+ });
+ }
+ },
+ removeElement: function removeElement(node) {
+ if (!utils.isElement(node)) {
+ return;
+ }
+
+ if (node.parentNode) {
+ node.parentNode.removeChild(node);
+ }
+ },
+ createWebWorker: function createWebWorker(content) {
+ if (!utils.isString(content)) {
+ return {};
+ }
+
+ try {
+ var blob = new utils.Blob([content], {
+ 'type': 'text/javascript'
+ });
+ var objectUrl = utils.URL.createObjectURL(blob);
+ var worker = new Worker(objectUrl);
+ return {
+ 'objectUrl': objectUrl,
+ 'worker': worker
+ };
+ } catch (e) {
+ return '' + e;
+ }
+ },
+ getExtension: function getExtension(src) {
+ return src.substr(src.lastIndexOf('.') + 1, src.length);
+ },
+ getFontSize: function getFontSize() {
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+
+ if (!document.body || options.resizeFont === false) {
+ return options.fontSize;
+ }
+
+ var text = options.text;
+ var containerWidth = options.gifWidth;
+ var fontSize = parseInt(options.fontSize, 10);
+ var minFontSize = parseInt(options.minFontSize, 10);
+ var div = document.createElement('div');
+ var span = document.createElement('span');
+ div.setAttribute('width', containerWidth);
+ div.appendChild(span);
+ span.innerHTML = text;
+ span.style.fontSize = fontSize + 'px';
+ span.style.textIndent = '-9999px';
+ span.style.visibility = 'hidden';
+ document.body.appendChild(span);
+
+ while (span.offsetWidth > containerWidth && fontSize >= minFontSize) {
+ span.style.fontSize = --fontSize + 'px';
+ }
+
+ document.body.removeChild(span);
+ return fontSize + 'px';
+ },
+ webWorkerError: false
+ };
+ var utils$2 = Object.freeze({
+ default: utils
+ });
+ /*
+ error.js
+ ========
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+ var error = {
+ validate: function validate(skipObj) {
+ skipObj = utils.isObject(skipObj) ? skipObj : {};
+ var errorObj = {};
+ utils.each(error.validators, function (indece, currentValidator) {
+ var errorCode = currentValidator.errorCode;
+
+ if (!skipObj[errorCode] && !currentValidator.condition) {
+ errorObj = currentValidator;
+ errorObj.error = true;
+ return false;
+ }
+ });
+ delete errorObj.condition;
+ return errorObj;
+ },
+ isValid: function isValid(skipObj) {
+ var errorObj = error.validate(skipObj);
+ var isValid = errorObj.error !== true ? true : false;
+ return isValid;
+ },
+ validators: [{
+ condition: utils.isFunction(utils.getUserMedia),
+ errorCode: 'getUserMedia',
+ errorMsg: 'The getUserMedia API is not supported in your browser'
+ }, {
+ condition: utils.isSupported.canvas(),
+ errorCode: 'canvas',
+ errorMsg: 'Canvas elements are not supported in your browser'
+ }, {
+ condition: utils.isSupported.webworkers(),
+ errorCode: 'webworkers',
+ errorMsg: 'The Web Workers API is not supported in your browser'
+ }, {
+ condition: utils.isFunction(utils.URL),
+ errorCode: 'window.URL',
+ errorMsg: 'The window.URL API is not supported in your browser'
+ }, {
+ condition: utils.isSupported.blob(),
+ errorCode: 'window.Blob',
+ errorMsg: 'The window.Blob File API is not supported in your browser'
+ }, {
+ condition: utils.isSupported.Uint8Array(),
+ errorCode: 'window.Uint8Array',
+ errorMsg: 'The window.Uint8Array function constructor is not supported in your browser'
+ }, {
+ condition: utils.isSupported.Uint32Array(),
+ errorCode: 'window.Uint32Array',
+ errorMsg: 'The window.Uint32Array function constructor is not supported in your browser'
+ }],
+ messages: {
+ videoCodecs: {
+ errorCode: 'videocodec',
+ errorMsg: 'The video codec you are trying to use is not supported in your browser'
+ }
+ }
+ };
+ var error$2 = Object.freeze({
+ default: error
+ });
+ /*
+ defaultOptions.js
+ =================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Helpers
+
+ var noop = function noop() {};
+
+ var defaultOptions = {
+ sampleInterval: 10,
+ numWorkers: 2,
+ filter: '',
+ gifWidth: 200,
+ gifHeight: 200,
+ interval: 0.1,
+ numFrames: 10,
+ frameDuration: 1,
+ keepCameraOn: false,
+ images: [],
+ video: null,
+ webcamVideoElement: null,
+ cameraStream: null,
+ text: '',
+ fontWeight: 'normal',
+ fontSize: '16px',
+ minFontSize: '10px',
+ resizeFont: false,
+ fontFamily: 'sans-serif',
+ fontColor: '#ffffff',
+ textAlign: 'center',
+ textBaseline: 'bottom',
+ textXCoordinate: null,
+ textYCoordinate: null,
+ progressCallback: noop,
+ completeCallback: noop,
+ saveRenderingContexts: false,
+ savedRenderingContexts: [],
+ crossOrigin: 'Anonymous'
+ };
+ var defaultOptions$2 = Object.freeze({
+ default: defaultOptions
+ });
+ /*
+ isSupported.js
+ ==============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+ function isSupported() {
+ return error.isValid();
+ }
+ /*
+ isWebCamGIFSupported.js
+ =======================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+
+ function isWebCamGIFSupported() {
+ return error.isValid();
+ }
+ /*
+ isSupported.js
+ ==============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function isSupported$1() {
+ var options = {
+ getUserMedia: true
+ };
+ return error.isValid(options);
+ }
+ /*
+ isExistingVideoGIFSupported.js
+ ==============================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function isExistingVideoGIFSupported(codecs) {
+ var hasValidCodec = false;
+
+ if (utils.isArray(codecs) && codecs.length) {
+ utils.each(codecs, function (indece, currentCodec) {
+ if (utils.isSupported.videoCodecs[currentCodec]) {
+ hasValidCodec = true;
+ }
+ });
+
+ if (!hasValidCodec) {
+ return false;
+ }
+ } else if (utils.isString(codecs) && codecs.length) {
+ if (!utils.isSupported.videoCodecs[codecs]) {
+ return false;
+ }
+ }
+
+ return error.isValid({
+ 'getUserMedia': true
+ });
+ }
+ /*
+ NeuQuant.js
+ ===========
+ */
+
+ /*
+ * NeuQuant Neural-Net Quantization Algorithm
+ * ------------------------------------------
+ *
+ * Copyright (c) 1994 Anthony Dekker
+ *
+ * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
+ * "Kohonen neural networks for optimal colour quantization" in "Network:
+ * Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
+ * the algorithm.
+ *
+ * Any party obtaining a copy of these files from the author, directly or
+ * indirectly, is granted, free of charge, a full and unrestricted irrevocable,
+ * world-wide, paid up, royalty-free, nonexclusive right and license to deal in
+ * this software and documentation files (the "Software"), including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons who
+ * receive copies from any such party to do so, with the only requirement being
+ * that this copyright notice remain intact.
+ */
+
+ /*
+ * This class handles Neural-Net quantization algorithm
+ * @author Kevin Weiner (original Java version - kweiner@fmsware.com)
+ * @author Thibault Imbert (AS3 version - bytearray.org)
+ * @version 0.1 AS3 implementation
+ * @version 0.2 JS->AS3 "translation" by antimatter15
+ * @version 0.3 JS clean up + using modern JS idioms by sole - http://soledadpenades.com
+ * Also implement fix in color conversion described at http://stackoverflow.com/questions/16371712/neuquant-js-javascript-color-quantization-hidden-bug-in-js-conversion
+ */
+
+
+ function NeuQuant() {
+ var netsize = 256; // number of colours used
+ // four primes near 500 - assume no image has a length so large
+ // that it is divisible by all four primes
+
+ var prime1 = 499;
+ var prime2 = 491;
+ var prime3 = 487;
+ var prime4 = 503; // minimum size for input image
+
+ var minpicturebytes = 3 * prime4; // Network Definitions
+
+ var maxnetpos = netsize - 1;
+ var netbiasshift = 4; // bias for colour values
+
+ var ncycles = 100; // no. of learning cycles
+ // defs for freq and bias
+
+ var intbiasshift = 16; // bias for fractions
+
+ var intbias = 1 << intbiasshift;
+ var gammashift = 10; // gamma = 1024
+ var betashift = 10;
+ var beta = intbias >> betashift; // beta = 1/1024
+
+ var betagamma = intbias << gammashift - betashift; // defs for decreasing radius factor
+ // For 256 colors, radius starts at 32.0 biased by 6 bits
+ // and decreases by a factor of 1/30 each cycle
+
+ var initrad = netsize >> 3;
+ var radiusbiasshift = 6;
+ var radiusbias = 1 << radiusbiasshift;
+ var initradius = initrad * radiusbias;
+ var radiusdec = 30; // defs for decreasing alpha factor
+ // Alpha starts at 1.0 biased by 10 bits
+
+ var alphabiasshift = 10;
+ var initalpha = 1 << alphabiasshift;
+ var alphadec; // radbias and alpharadbias used for radpower calculation
+
+ var radbiasshift = 8;
+ var radbias = 1 << radbiasshift;
+ var alpharadbshift = alphabiasshift + radbiasshift;
+ var alpharadbias = 1 << alpharadbshift; // Input image
+
+ var thepicture; // Height * Width * 3
+
+ var lengthcount; // Sampling factor 1..30
+
+ var samplefac; // The network itself
+
+ var network;
+ var netindex = []; // for network lookup - really 256
+
+ var bias = []; // bias and freq arrays for learning
+
+ var freq = [];
+ var radpower = [];
+
+ function NeuQuantConstructor(thepic, len, sample) {
+ var i;
+ var p;
+ thepicture = thepic;
+ lengthcount = len;
+ samplefac = sample;
+ network = new Array(netsize);
+
+ for (i = 0; i < netsize; i++) {
+ network[i] = new Array(4);
+ p = network[i];
+ p[0] = p[1] = p[2] = (i << netbiasshift + 8) / netsize | 0;
+ freq[i] = intbias / netsize | 0; // 1 / netsize
+
+ bias[i] = 0;
+ }
+ }
+
+ function colorMap() {
+ var map = [];
+ var index = new Array(netsize);
+
+ for (var i = 0; i < netsize; i++) {
+ index[network[i][3]] = i;
+ }
+
+ var k = 0;
+
+ for (var l = 0; l < netsize; l++) {
+ var j = index[l];
+ map[k++] = network[j][0];
+ map[k++] = network[j][1];
+ map[k++] = network[j][2];
+ }
+
+ return map;
+ } // Insertion sort of network and building of netindex[0..255]
+ // (to do after unbias)
+
+
+ function inxbuild() {
+ var i;
+ var j;
+ var smallpos;
+ var smallval;
+ var p;
+ var q;
+ var previouscol;
+ var startpos;
+ previouscol = 0;
+ startpos = 0;
+
+ for (i = 0; i < netsize; i++) {
+ p = network[i];
+ smallpos = i;
+ smallval = p[1]; // index on g
+ // find smallest in i..netsize-1
+
+ for (j = i + 1; j < netsize; j++) {
+ q = network[j];
+
+ if (q[1] < smallval) {
+ // index on g
+ smallpos = j;
+ smallval = q[1]; // index on g
+ }
+ }
+
+ q = network[smallpos]; // swap p (i) and q (smallpos) entries
+
+ if (i != smallpos) {
+ j = q[0];
+ q[0] = p[0];
+ p[0] = j;
+ j = q[1];
+ q[1] = p[1];
+ p[1] = j;
+ j = q[2];
+ q[2] = p[2];
+ p[2] = j;
+ j = q[3];
+ q[3] = p[3];
+ p[3] = j;
+ } // smallval entry is now in position i
+
+
+ if (smallval != previouscol) {
+ netindex[previouscol] = startpos + i >> 1;
+
+ for (j = previouscol + 1; j < smallval; j++) {
+ netindex[j] = i;
+ }
+
+ previouscol = smallval;
+ startpos = i;
+ }
+ }
+
+ netindex[previouscol] = startpos + maxnetpos >> 1;
+
+ for (j = previouscol + 1; j < 256; j++) {
+ netindex[j] = maxnetpos; // really 256
+ }
+ } // Main Learning Loop
+
+
+ function learn() {
+ var i;
+ var j;
+ var b;
+ var g;
+ var r;
+ var radius;
+ var rad;
+ var alpha;
+ var step;
+ var delta;
+ var samplepixels;
+ var p;
+ var pix;
+ var lim;
+
+ if (lengthcount < minpicturebytes) {
+ samplefac = 1;
+ }
+
+ alphadec = 30 + (samplefac - 1) / 3;
+ p = thepicture;
+ pix = 0;
+ lim = lengthcount;
+ samplepixels = lengthcount / (3 * samplefac);
+ delta = samplepixels / ncycles | 0;
+ alpha = initalpha;
+ radius = initradius;
+ rad = radius >> radiusbiasshift;
+
+ if (rad <= 1) {
+ rad = 0;
+ }
+
+ for (i = 0; i < rad; i++) {
+ radpower[i] = alpha * ((rad * rad - i * i) * radbias / (rad * rad));
+ }
+
+ if (lengthcount < minpicturebytes) {
+ step = 3;
+ } else if (lengthcount % prime1 !== 0) {
+ step = 3 * prime1;
+ } else {
+ if (lengthcount % prime2 !== 0) {
+ step = 3 * prime2;
+ } else {
+ if (lengthcount % prime3 !== 0) {
+ step = 3 * prime3;
+ } else {
+ step = 3 * prime4;
+ }
+ }
+ }
+
+ i = 0;
+
+ while (i < samplepixels) {
+ b = (p[pix + 0] & 0xff) << netbiasshift;
+ g = (p[pix + 1] & 0xff) << netbiasshift;
+ r = (p[pix + 2] & 0xff) << netbiasshift;
+ j = contest(b, g, r);
+ altersingle(alpha, j, b, g, r);
+
+ if (rad !== 0) {
+ // Alter neighbours
+ alterneigh(rad, j, b, g, r);
+ }
+
+ pix += step;
+
+ if (pix >= lim) {
+ pix -= lengthcount;
+ }
+
+ i++;
+
+ if (delta === 0) {
+ delta = 1;
+ }
+
+ if (i % delta === 0) {
+ alpha -= alpha / alphadec;
+ radius -= radius / radiusdec;
+ rad = radius >> radiusbiasshift;
+
+ if (rad <= 1) {
+ rad = 0;
+ }
+
+ for (j = 0; j < rad; j++) {
+ radpower[j] = alpha * ((rad * rad - j * j) * radbias / (rad * rad));
+ }
+ }
+ }
+ } // Search for BGR values 0..255 (after net is unbiased) and return colour index
+
+
+ function map(b, g, r) {
+ var i;
+ var j;
+ var dist;
+ var a;
+ var bestd;
+ var p;
+ var best; // Biggest possible distance is 256 * 3
+
+ bestd = 1000;
+ best = -1;
+ i = netindex[g]; // index on g
+
+ j = i - 1; // start at netindex[g] and work outwards
+
+ while (i < netsize || j >= 0) {
+ if (i < netsize) {
+ p = network[i];
+ dist = p[1] - g; // inx key
+
+ if (dist >= bestd) {
+ i = netsize; // stop iter
+ } else {
+ i++;
+
+ if (dist < 0) {
+ dist = -dist;
+ }
+
+ a = p[0] - b;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+
+ if (dist < bestd) {
+ a = p[2] - r;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+
+ if (dist < bestd) {
+ bestd = dist;
+ best = p[3];
+ }
+ }
+ }
+ }
+
+ if (j >= 0) {
+ p = network[j];
+ dist = g - p[1]; // inx key - reverse dif
+
+ if (dist >= bestd) {
+ j = -1; // stop iter
+ } else {
+ j--;
+
+ if (dist < 0) {
+ dist = -dist;
+ }
+
+ a = p[0] - b;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+
+ if (dist < bestd) {
+ a = p[2] - r;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+
+ if (dist < bestd) {
+ bestd = dist;
+ best = p[3];
+ }
+ }
+ }
+ }
+ }
+
+ return best;
+ }
+
+ function process() {
+ learn();
+ unbiasnet();
+ inxbuild();
+ return colorMap();
+ } // Unbias network to give byte values 0..255 and record position i
+ // to prepare for sort
+
+
+ function unbiasnet() {
+ var i;
+
+ for (i = 0; i < netsize; i++) {
+ network[i][0] >>= netbiasshift;
+ network[i][1] >>= netbiasshift;
+ network[i][2] >>= netbiasshift;
+ network[i][3] = i; // record colour no
+ }
+ } // Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2))
+ // in radpower[|i-j|]
+
+
+ function alterneigh(rad, i, b, g, r) {
+ var j;
+ var k;
+ var lo;
+ var hi;
+ var a;
+ var m;
+ var p;
+ lo = i - rad;
+
+ if (lo < -1) {
+ lo = -1;
+ }
+
+ hi = i + rad;
+
+ if (hi > netsize) {
+ hi = netsize;
+ }
+
+ j = i + 1;
+ k = i - 1;
+ m = 1;
+
+ while (j < hi || k > lo) {
+ a = radpower[m++];
+
+ if (j < hi) {
+ p = network[j++];
+
+ try {
+ p[0] -= a * (p[0] - b) / alpharadbias | 0;
+ p[1] -= a * (p[1] - g) / alpharadbias | 0;
+ p[2] -= a * (p[2] - r) / alpharadbias | 0;
+ } catch (e) {}
+ }
+
+ if (k > lo) {
+ p = network[k--];
+
+ try {
+ p[0] -= a * (p[0] - b) / alpharadbias | 0;
+ p[1] -= a * (p[1] - g) / alpharadbias | 0;
+ p[2] -= a * (p[2] - r) / alpharadbias | 0;
+ } catch (e) {}
+ }
+ }
+ } // Move neuron i towards biased (b,g,r) by factor alpha
+
+
+ function altersingle(alpha, i, b, g, r) {
+ // alter hit neuron
+ var n = network[i];
+ var alphaMult = alpha / initalpha;
+ n[0] -= alphaMult * (n[0] - b) | 0;
+ n[1] -= alphaMult * (n[1] - g) | 0;
+ n[2] -= alphaMult * (n[2] - r) | 0;
+ } // Search for biased BGR values
+
+
+ function contest(b, g, r) {
+ // finds closest neuron (min dist) and updates freq
+ // finds best neuron (min dist-bias) and returns position
+ // for frequently chosen neurons, freq[i] is high and bias[i] is negative
+ // bias[i] = gamma*((1/netsize)-freq[i])
+ var i;
+ var dist;
+ var a;
+ var biasdist;
+ var betafreq;
+ var bestpos;
+ var bestbiaspos;
+ var bestd;
+ var bestbiasd;
+ var n;
+ bestd = ~(1 << 31);
+ bestbiasd = bestd;
+ bestpos = -1;
+ bestbiaspos = bestpos;
+
+ for (i = 0; i < netsize; i++) {
+ n = network[i];
+ dist = n[0] - b;
+
+ if (dist < 0) {
+ dist = -dist;
+ }
+
+ a = n[1] - g;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+ a = n[2] - r;
+
+ if (a < 0) {
+ a = -a;
+ }
+
+ dist += a;
+
+ if (dist < bestd) {
+ bestd = dist;
+ bestpos = i;
+ }
+
+ biasdist = dist - (bias[i] >> intbiasshift - netbiasshift);
+
+ if (biasdist < bestbiasd) {
+ bestbiasd = biasdist;
+ bestbiaspos = i;
+ }
+
+ betafreq = freq[i] >> betashift;
+ freq[i] -= betafreq;
+ bias[i] += betafreq << gammashift;
+ }
+
+ freq[bestpos] += beta;
+ bias[bestpos] -= betagamma;
+ return bestbiaspos;
+ }
+
+ NeuQuantConstructor.apply(this, arguments);
+ var exports = {};
+ exports.map = map;
+ exports.process = process;
+ return exports;
+ }
+ /*
+ processFrameWorker.js
+ =====================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+
+ function workerCode() {
+ var self = this;
+
+ try {
+ self.onmessage = function (ev) {
+ var data = ev.data || {};
+ var response;
+
+ if (data.gifshot) {
+ response = workerMethods.run(data);
+ postMessage(response);
+ }
+ };
+ } catch (e) {}
+
+ var workerMethods = {
+ dataToRGB: function dataToRGB(data, width, height) {
+ var length = width * height * 4;
+ var i = 0;
+ var rgb = [];
+
+ while (i < length) {
+ rgb.push(data[i++]);
+ rgb.push(data[i++]);
+ rgb.push(data[i++]);
+ i++; // for the alpha channel which we don't care about
+ }
+
+ return rgb;
+ },
+ componentizedPaletteToArray: function componentizedPaletteToArray(paletteRGB) {
+ paletteRGB = paletteRGB || [];
+ var paletteArray = [];
+
+ for (var i = 0; i < paletteRGB.length; i += 3) {
+ var r = paletteRGB[i];
+ var g = paletteRGB[i + 1];
+ var b = paletteRGB[i + 2];
+ paletteArray.push(r << 16 | g << 8 | b);
+ }
+
+ return paletteArray;
+ },
+ // This is the "traditional" Animated_GIF style of going from RGBA to indexed color frames
+ 'processFrameWithQuantizer': function processFrameWithQuantizer(imageData, width, height, sampleInterval) {
+ var rgbComponents = this.dataToRGB(imageData, width, height);
+ var nq = new NeuQuant(rgbComponents, rgbComponents.length, sampleInterval);
+ var paletteRGB = nq.process();
+ var paletteArray = new Uint32Array(this.componentizedPaletteToArray(paletteRGB));
+ var numberPixels = width * height;
+ var indexedPixels = new Uint8Array(numberPixels);
+ var k = 0;
+
+ for (var i = 0; i < numberPixels; i++) {
+ var r = rgbComponents[k++];
+ var g = rgbComponents[k++];
+ var b = rgbComponents[k++];
+ indexedPixels[i] = nq.map(r, g, b);
+ }
+
+ return {
+ pixels: indexedPixels,
+ palette: paletteArray
+ };
+ },
+ 'run': function run(frame) {
+ frame = frame || {};
+ var _frame = frame,
+ height = _frame.height,
+ palette = _frame.palette,
+ sampleInterval = _frame.sampleInterval,
+ width = _frame.width;
+ var imageData = frame.data;
+ return this.processFrameWithQuantizer(imageData, width, height, sampleInterval);
+ }
+ };
+ return workerMethods;
+ }
+ /*
+ gifWriter.js
+ ============
+ */
+ // (c) Dean McNamee , 2013.
+ //
+ // https://github.com/deanm/omggif
+ //
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
+ // of this software and associated documentation files (the "Software"), to
+ // deal in the Software without restriction, including without limitation the
+ // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ // sell copies of the Software, and to permit persons to whom the Software is
+ // furnished to do so, subject to the following conditions:
+ //
+ // The above copyright notice and this permission notice shall be included in
+ // all copies or substantial portions of the Software.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ // IN THE SOFTWARE.
+ //
+ // omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
+ // including animation and compression. It does not rely on any specific
+ // underlying system, so should run in the browser, Node, or Plask.
+
+
+ function gifWriter(buf, width, height, gopts) {
+ var p = 0;
+ gopts = gopts === undefined ? {} : gopts;
+ var loop_count = gopts.loop === undefined ? null : gopts.loop;
+ var global_palette = gopts.palette === undefined ? null : gopts.palette;
+ if (width <= 0 || height <= 0 || width > 65535 || height > 65535) throw "Width/Height invalid.";
+
+ function check_palette_and_num_colors(palette) {
+ var num_colors = palette.length;
+ if (num_colors < 2 || num_colors > 256 || num_colors & num_colors - 1) throw "Invalid code/color length, must be power of 2 and 2 .. 256.";
+ return num_colors;
+ } // - Header.
+
+
+ buf[p++] = 0x47;
+ buf[p++] = 0x49;
+ buf[p++] = 0x46; // GIF
+
+ buf[p++] = 0x38;
+ buf[p++] = 0x39;
+ buf[p++] = 0x61; // 89a
+ // Handling of Global Color Table (palette) and background index.
+
+ var gp_num_colors_pow2 = 0;
+ var background = 0; // - Logical Screen Descriptor.
+ // NOTE(deanm): w/h apparently ignored by implementations, but set anyway.
+
+ buf[p++] = width & 0xff;
+ buf[p++] = width >> 8 & 0xff;
+ buf[p++] = height & 0xff;
+ buf[p++] = height >> 8 & 0xff; // NOTE: Indicates 0-bpp original color resolution (unused?).
+
+ buf[p++] = (global_palette !== null ? 0x80 : 0) | // Global Color Table Flag.
+ gp_num_colors_pow2; // NOTE: No sort flag (unused?).
+
+ buf[p++] = background; // Background Color Index.
+
+ buf[p++] = 0; // Pixel aspect ratio (unused?).
+
+ if (loop_count !== null) {
+ // Netscape block for looping.
+ if (loop_count < 0 || loop_count > 65535) throw "Loop count invalid."; // Extension code, label, and length.
+
+ buf[p++] = 0x21;
+ buf[p++] = 0xff;
+ buf[p++] = 0x0b; // NETSCAPE2.0
+
+ buf[p++] = 0x4e;
+ buf[p++] = 0x45;
+ buf[p++] = 0x54;
+ buf[p++] = 0x53;
+ buf[p++] = 0x43;
+ buf[p++] = 0x41;
+ buf[p++] = 0x50;
+ buf[p++] = 0x45;
+ buf[p++] = 0x32;
+ buf[p++] = 0x2e;
+ buf[p++] = 0x30; // Sub-block
+
+ buf[p++] = 0x03;
+ buf[p++] = 0x01;
+ buf[p++] = loop_count & 0xff;
+ buf[p++] = loop_count >> 8 & 0xff;
+ buf[p++] = 0x00; // Terminator.
+ }
+
+ var ended = false;
+
+ this.addFrame = function (x, y, w, h, indexed_pixels, opts) {
+ if (ended === true) {
+ --p;
+ ended = false;
+ } // Un-end.
+
+
+ opts = opts === undefined ? {} : opts; // TODO(deanm): Bounds check x, y. Do they need to be within the virtual
+ // canvas width/height, I imagine?
+
+ if (x < 0 || y < 0 || x > 65535 || y > 65535) throw "x/y invalid.";
+ if (w <= 0 || h <= 0 || w > 65535 || h > 65535) throw "Width/Height invalid.";
+ if (indexed_pixels.length < w * h) throw "Not enough pixels for the frame size.";
+ var using_local_palette = true;
+ var palette = opts.palette;
+
+ if (palette === undefined || palette === null) {
+ using_local_palette = false;
+ palette = global_palette;
+ }
+
+ if (palette === undefined || palette === null) throw "Must supply either a local or global palette.";
+ var num_colors = check_palette_and_num_colors(palette); // Compute the min_code_size (power of 2), destroying num_colors.
+
+ var min_code_size = 0;
+
+ while (num_colors >>= 1) {
+ ++min_code_size;
+ }
+
+ num_colors = 1 << min_code_size; // Now we can easily get it back.
+
+ var delay = opts.delay === undefined ? 0 : opts.delay; // From the spec:
+ // 0 - No disposal specified. The decoder is
+ // not required to take any action.
+ // 1 - Do not dispose. The graphic is to be left
+ // in place.
+ // 2 - Restore to background color. The area used by the
+ // graphic must be restored to the background color.
+ // 3 - Restore to previous. The decoder is required to
+ // restore the area overwritten by the graphic with
+ // what was there prior to rendering the graphic.
+ // 4-7 - To be defined.
+ // NOTE(deanm): Dispose background doesn't really work, apparently most
+ // browsers ignore the background palette index and clear to transparency.
+
+ var disposal = opts.disposal === undefined ? 0 : opts.disposal;
+ if (disposal < 0 || disposal > 3) // 4-7 is reserved.
+ throw "Disposal out of range.";
+ var use_transparency = false;
+ var transparent_index = 0;
+
+ if (opts.transparent !== undefined && opts.transparent !== null) {
+ use_transparency = true;
+ transparent_index = opts.transparent;
+ if (transparent_index < 0 || transparent_index >= num_colors) throw "Transparent color index.";
+ }
+
+ if (disposal !== 0 || use_transparency || delay !== 0) {
+ // - Graphics Control Extension
+ buf[p++] = 0x21;
+ buf[p++] = 0xf9; // Extension / Label.
+
+ buf[p++] = 4; // Byte size.
+
+ buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0);
+ buf[p++] = delay & 0xff;
+ buf[p++] = delay >> 8 & 0xff;
+ buf[p++] = transparent_index; // Transparent color index.
+
+ buf[p++] = 0; // Block Terminator.
+ } // - Image Descriptor
+
+
+ buf[p++] = 0x2c; // Image Seperator.
+
+ buf[p++] = x & 0xff;
+ buf[p++] = x >> 8 & 0xff; // Left.
+
+ buf[p++] = y & 0xff;
+ buf[p++] = y >> 8 & 0xff; // Top.
+
+ buf[p++] = w & 0xff;
+ buf[p++] = w >> 8 & 0xff;
+ buf[p++] = h & 0xff;
+ buf[p++] = h >> 8 & 0xff; // NOTE: No sort flag (unused?).
+ // TODO(deanm): Support interlace.
+
+ buf[p++] = using_local_palette === true ? 0x80 | min_code_size - 1 : 0; // - Local Color Table
+
+ if (using_local_palette === true) {
+ for (var i = 0, il = palette.length; i < il; ++i) {
+ var rgb = palette[i];
+ buf[p++] = rgb >> 16 & 0xff;
+ buf[p++] = rgb >> 8 & 0xff;
+ buf[p++] = rgb & 0xff;
+ }
+ }
+
+ p = GifWriterOutputLZWCodeStream(buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels);
+ };
+
+ this.end = function () {
+ if (ended === false) {
+ buf[p++] = 0x3b; // Trailer.
+
+ ended = true;
+ }
+
+ return p;
+ }; // Main compression routine, palette indexes -> LZW code stream.
+ // |index_stream| must have at least one entry.
+
+
+ function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) {
+ buf[p++] = min_code_size;
+ var cur_subblock = p++; // Pointing at the length field.
+
+ var clear_code = 1 << min_code_size;
+ var code_mask = clear_code - 1;
+ var eoi_code = clear_code + 1;
+ var next_code = eoi_code + 1;
+ var cur_code_size = min_code_size + 1; // Number of bits per code.
+
+ var cur_shift = 0; // We have at most 12-bit codes, so we should have to hold a max of 19
+ // bits here (and then we would write out).
+
+ var cur = 0;
+
+ function emit_bytes_to_buffer(bit_block_size) {
+ while (cur_shift >= bit_block_size) {
+ buf[p++] = cur & 0xff;
+ cur >>= 8;
+ cur_shift -= 8;
+
+ if (p === cur_subblock + 256) {
+ // Finished a subblock.
+ buf[cur_subblock] = 255;
+ cur_subblock = p++;
+ }
+ }
+ }
+
+ function emit_code(c) {
+ cur |= c << cur_shift;
+ cur_shift += cur_code_size;
+ emit_bytes_to_buffer(8);
+ } // I am not an expert on the topic, and I don't want to write a thesis.
+ // However, it is good to outline here the basic algorithm and the few data
+ // structures and optimizations here that make this implementation fast.
+ // The basic idea behind LZW is to build a table of previously seen runs
+ // addressed by a short id (herein called output code). All data is
+ // referenced by a code, which represents one or more values from the
+ // original input stream. All input bytes can be referenced as the same
+ // value as an output code. So if you didn't want any compression, you
+ // could more or less just output the original bytes as codes (there are
+ // some details to this, but it is the idea). In order to achieve
+ // compression, values greater then the input range (codes can be up to
+ // 12-bit while input only 8-bit) represent a sequence of previously seen
+ // inputs. The decompressor is able to build the same mapping while
+ // decoding, so there is always a shared common knowledge between the
+ // encoding and decoder, which is also important for "timing" aspects like
+ // how to handle variable bit width code encoding.
+ //
+ // One obvious but very important consequence of the table system is there
+ // is always a unique id (at most 12-bits) to map the runs. 'A' might be
+ // 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc. This relationship
+ // can be used for an effecient lookup strategy for the code mapping. We
+ // need to know if a run has been seen before, and be able to map that run
+ // to the output code. Since we start with known unique ids (input bytes),
+ // and then from those build more unique ids (table entries), we can
+ // continue this chain (almost like a linked list) to always have small
+ // integer values that represent the current byte chains in the encoder.
+ // This means instead of tracking the input bytes (AAAABCD) to know our
+ // current state, we can track the table entry for AAAABC (it is guaranteed
+ // to exist by the nature of the algorithm) and the next character D.
+ // Therefor the tuple of (table_entry, byte) is guaranteed to also be
+ // unique. This allows us to create a simple lookup key for mapping input
+ // sequences to codes (table indices) without having to store or search
+ // any of the code sequences. So if 'AAAA' has a table entry of 12, the
+ // tuple of ('AAAA', K) for any input byte K will be unique, and can be our
+ // key. This leads to a integer value at most 20-bits, which can always
+ // fit in an SMI value and be used as a fast sparse array / object key.
+ // Output code for the current contents of the index buffer.
+
+
+ var ib_code = index_stream[0] & code_mask; // Load first input index.
+
+ var code_table = {}; // Key'd on our 20-bit "tuple".
+
+ emit_code(clear_code); // Spec says first code should be a clear code.
+ // First index already loaded, process the rest of the stream.
+
+ for (var i = 1, il = index_stream.length; i < il; ++i) {
+ var k = index_stream[i] & code_mask;
+ var cur_key = ib_code << 8 | k; // (prev, k) unique tuple.
+
+ var cur_code = code_table[cur_key]; // buffer + k.
+ // Check if we have to create a new code table entry.
+
+ if (cur_code === undefined) {
+ // We don't have buffer + k.
+ // Emit index buffer (without k).
+ // This is an inline version of emit_code, because this is the core
+ // writing routine of the compressor (and V8 cannot inline emit_code
+ // because it is a closure here in a different context). Additionally
+ // we can call emit_byte_to_buffer less often, because we can have
+ // 30-bits (from our 31-bit signed SMI), and we know our codes will only
+ // be 12-bits, so can safely have 18-bits there without overflow.
+ // emit_code(ib_code);
+ cur |= ib_code << cur_shift;
+ cur_shift += cur_code_size;
+
+ while (cur_shift >= 8) {
+ buf[p++] = cur & 0xff;
+ cur >>= 8;
+ cur_shift -= 8;
+
+ if (p === cur_subblock + 256) {
+ // Finished a subblock.
+ buf[cur_subblock] = 255;
+ cur_subblock = p++;
+ }
+ }
+
+ if (next_code === 4096) {
+ // Table full, need a clear.
+ emit_code(clear_code);
+ next_code = eoi_code + 1;
+ cur_code_size = min_code_size + 1;
+ code_table = {};
+ } else {
+ // Table not full, insert a new entry.
+ // Increase our variable bit code sizes if necessary. This is a bit
+ // tricky as it is based on "timing" between the encoding and
+ // decoder. From the encoders perspective this should happen after
+ // we've already emitted the index buffer and are about to create the
+ // first table entry that would overflow our current code bit size.
+ if (next_code >= 1 << cur_code_size) ++cur_code_size;
+ code_table[cur_key] = next_code++; // Insert into code table.
+ }
+
+ ib_code = k; // Index buffer to single input k.
+ } else {
+ ib_code = cur_code; // Index buffer to sequence in code table.
+ }
+ }
+
+ emit_code(ib_code); // There will still be something in the index buffer.
+
+ emit_code(eoi_code); // End Of Information.
+ // Flush / finalize the sub-blocks stream to the buffer.
+
+ emit_bytes_to_buffer(1); // Finish the sub-blocks, writing out any unfinished lengths and
+ // terminating with a sub-block of length 0. If we have already started
+ // but not yet used a sub-block it can just become the terminator.
+
+ if (cur_subblock + 1 === p) {
+ // Started but unused.
+ buf[cur_subblock] = 0;
+ } else {
+ // Started and used, write length and additional terminator block.
+ buf[cur_subblock] = p - cur_subblock - 1;
+ buf[p++] = 0;
+ }
+
+ return p;
+ }
+ }
+ /*
+ animatedGIF.js
+ ==============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+ // Helpers
+
+
+ var noop$2 = function noop() {};
+
+ var AnimatedGIF = function AnimatedGIF(options) {
+ this.canvas = null;
+ this.ctx = null;
+ this.repeat = 0;
+ this.frames = [];
+ this.numRenderedFrames = 0;
+ this.onRenderCompleteCallback = noop$2;
+ this.onRenderProgressCallback = noop$2;
+ this.workers = [];
+ this.availableWorkers = [];
+ this.generatingGIF = false;
+ this.options = options; // Constructs and initializes the the web workers appropriately
+
+ this.initializeWebWorkers(options);
+ };
+
+ AnimatedGIF.prototype = {
+ 'workerMethods': workerCode(),
+ 'initializeWebWorkers': function initializeWebWorkers(options) {
+ var self = this;
+ var processFrameWorkerCode = NeuQuant.toString() + '(' + workerCode.toString() + '());';
+ var webWorkerObj = void 0;
+ var objectUrl = void 0;
+ var webWorker = void 0;
+ var numWorkers = void 0;
+ var x = -1;
+ var workerError = '';
+ numWorkers = options.numWorkers;
+
+ while (++x < numWorkers) {
+ webWorkerObj = utils.createWebWorker(processFrameWorkerCode);
+
+ if (utils.isObject(webWorkerObj)) {
+ objectUrl = webWorkerObj.objectUrl;
+ webWorker = webWorkerObj.worker;
+ self.workers.push({
+ worker: webWorker,
+ objectUrl: objectUrl
+ });
+ self.availableWorkers.push(webWorker);
+ } else {
+ workerError = webWorkerObj;
+ utils.webWorkerError = !!webWorkerObj;
+ }
+ }
+
+ this.workerError = workerError;
+ this.canvas = document.createElement('canvas');
+ this.canvas.width = options.gifWidth;
+ this.canvas.height = options.gifHeight;
+ this.ctx = this.canvas.getContext('2d');
+ this.frames = [];
+ },
+ // Return a worker for processing a frame
+ getWorker: function getWorker() {
+ return this.availableWorkers.pop();
+ },
+ // Restores a worker to the pool
+ freeWorker: function freeWorker(worker) {
+ this.availableWorkers.push(worker);
+ },
+ byteMap: function () {
+ var byteMap = [];
+
+ for (var i = 0; i < 256; i++) {
+ byteMap[i] = String.fromCharCode(i);
+ }
+
+ return byteMap;
+ }(),
+ bufferToString: function bufferToString(buffer) {
+ var numberValues = buffer.length;
+ var str = '';
+ var x = -1;
+
+ while (++x < numberValues) {
+ str += this.byteMap[buffer[x]];
+ }
+
+ return str;
+ },
+ onFrameFinished: function onFrameFinished(progressCallback) {
+ // The GIF is not written until we're done with all the frames
+ // because they might not be processed in the same order
+ var self = this;
+ var frames = self.frames;
+ var options = self.options;
+ var hasExistingImages = !!(options.images || []).length;
+ var allDone = frames.every(function (frame) {
+ return !frame.beingProcessed && frame.done;
+ });
+ self.numRenderedFrames++;
+
+ if (hasExistingImages) {
+ progressCallback(self.numRenderedFrames / frames.length);
+ }
+
+ self.onRenderProgressCallback(self.numRenderedFrames * 0.75 / frames.length);
+
+ if (allDone) {
+ if (!self.generatingGIF) {
+ self.generateGIF(frames, self.onRenderCompleteCallback);
+ }
+ } else {
+ utils.requestTimeout(function () {
+ self.processNextFrame();
+ }, 1);
+ }
+ },
+ processFrame: function processFrame(position) {
+ var AnimatedGifContext = this;
+ var options = this.options;
+ var _options = this.options,
+ progressCallback = _options.progressCallback,
+ sampleInterval = _options.sampleInterval;
+ var frames = this.frames;
+ var frame = void 0;
+ var worker = void 0;
+
+ var done = function done() {
+ var ev = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var data = ev.data; // Delete original data, and free memory
+
+ delete frame.data;
+ frame.pixels = Array.prototype.slice.call(data.pixels);
+ frame.palette = Array.prototype.slice.call(data.palette);
+ frame.done = true;
+ frame.beingProcessed = false;
+ AnimatedGifContext.freeWorker(worker);
+ AnimatedGifContext.onFrameFinished(progressCallback);
+ };
+
+ frame = frames[position];
+
+ if (frame.beingProcessed || frame.done) {
+ this.onFrameFinished();
+ return;
+ }
+
+ frame.sampleInterval = sampleInterval;
+ frame.beingProcessed = true;
+ frame.gifshot = true;
+ worker = this.getWorker();
+
+ if (worker) {
+ // Process the frame in a web worker
+ worker.onmessage = done;
+ worker.postMessage(frame);
+ } else {
+ // Process the frame in the current thread
+ done({
+ 'data': AnimatedGifContext.workerMethods.run(frame)
+ });
+ }
+ },
+ startRendering: function startRendering(completeCallback) {
+ this.onRenderCompleteCallback = completeCallback;
+
+ for (var i = 0; i < this.options.numWorkers && i < this.frames.length; i++) {
+ this.processFrame(i);
+ }
+ },
+ processNextFrame: function processNextFrame() {
+ var position = -1;
+
+ for (var i = 0; i < this.frames.length; i++) {
+ var frame = this.frames[i];
+
+ if (!frame.done && !frame.beingProcessed) {
+ position = i;
+ break;
+ }
+ }
+
+ if (position >= 0) {
+ this.processFrame(position);
+ }
+ },
+ // Takes the already processed data in frames and feeds it to a new
+ // GifWriter instance in order to get the binary GIF file
+ generateGIF: function generateGIF(frames, callback) {
+ // TODO: Weird: using a simple JS array instead of a typed array,
+ // the files are WAY smaller o_o. Patches/explanations welcome!
+ var buffer = []; // new Uint8Array(width * height * frames.length * 5);
+
+ var gifOptions = {
+ loop: this.repeat
+ };
+ var options = this.options;
+ var interval = options.interval;
+ var frameDuration = options.frameDuration;
+ var existingImages = options.images;
+ var hasExistingImages = !!existingImages.length;
+ var height = options.gifHeight;
+ var width = options.gifWidth;
+ var gifWriter$$1 = new gifWriter(buffer, width, height, gifOptions);
+ var onRenderProgressCallback = this.onRenderProgressCallback;
+ var delay = hasExistingImages ? interval * 100 : 0;
+ var bufferToString = void 0;
+ var gif = void 0;
+ this.generatingGIF = true;
+ utils.each(frames, function (iterator, frame) {
+ var framePalette = frame.palette;
+ onRenderProgressCallback(0.75 + 0.25 * frame.position * 1.0 / frames.length);
+
+ for (var i = 0; i < frameDuration; i++) {
+ gifWriter$$1.addFrame(0, 0, width, height, frame.pixels, {
+ palette: framePalette,
+ delay: delay
+ });
+ }
+ });
+ gifWriter$$1.end();
+ onRenderProgressCallback(1.0);
+ this.frames = [];
+ this.generatingGIF = false;
+
+ if (utils.isFunction(callback)) {
+ bufferToString = this.bufferToString(buffer);
+ gif = 'data:image/gif;base64,' + utils.btoa(bufferToString);
+ callback(gif);
+ }
+ },
+ // From GIF: 0 = loop forever, null = not looping, n > 0 = loop n times and stop
+ setRepeat: function setRepeat(r) {
+ this.repeat = r;
+ },
+ addFrame: function addFrame(element, gifshotOptions) {
+ gifshotOptions = utils.isObject(gifshotOptions) ? gifshotOptions : {};
+ var self = this;
+ var ctx = self.ctx;
+ var options = self.options;
+ var width = options.gifWidth;
+ var height = options.gifHeight;
+ var fontSize = utils.getFontSize(gifshotOptions);
+ var _gifshotOptions = gifshotOptions,
+ filter = _gifshotOptions.filter,
+ fontColor = _gifshotOptions.fontColor,
+ fontFamily = _gifshotOptions.fontFamily,
+ fontWeight = _gifshotOptions.fontWeight,
+ gifHeight = _gifshotOptions.gifHeight,
+ gifWidth = _gifshotOptions.gifWidth,
+ text = _gifshotOptions.text,
+ textAlign = _gifshotOptions.textAlign,
+ textBaseline = _gifshotOptions.textBaseline;
+ var textXCoordinate = gifshotOptions.textXCoordinate ? gifshotOptions.textXCoordinate : textAlign === 'left' ? 1 : textAlign === 'right' ? width : width / 2;
+ var textYCoordinate = gifshotOptions.textYCoordinate ? gifshotOptions.textYCoordinate : textBaseline === 'top' ? 1 : textBaseline === 'center' ? height / 2 : height;
+ var font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
+ var imageData = void 0;
+
+ try {
+ ctx.filter = filter;
+ ctx.drawImage(element, 0, 0, width, height);
+
+ if (text) {
+ ctx.font = font;
+ ctx.fillStyle = fontColor;
+ ctx.textAlign = textAlign;
+ ctx.textBaseline = textBaseline;
+ ctx.fillText(text, textXCoordinate, textYCoordinate);
+ }
+
+ imageData = ctx.getImageData(0, 0, width, height);
+ self.addFrameImageData(imageData);
+ } catch (e) {
+ return '' + e;
+ }
+ },
+ addFrameImageData: function addFrameImageData() {
+ var imageData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var frames = this.frames;
+ var imageDataArray = imageData.data;
+ this.frames.push({
+ 'data': imageDataArray,
+ 'width': imageData.width,
+ 'height': imageData.height,
+ 'palette': null,
+ 'dithering': null,
+ 'done': false,
+ 'beingProcessed': false,
+ 'position': frames.length
+ });
+ },
+ onRenderProgress: function onRenderProgress(callback) {
+ this.onRenderProgressCallback = callback;
+ },
+ isRendering: function isRendering() {
+ return this.generatingGIF;
+ },
+ getBase64GIF: function getBase64GIF(completeCallback) {
+ var self = this;
+
+ var onRenderComplete = function onRenderComplete(gif) {
+ self.destroyWorkers();
+ utils.requestTimeout(function () {
+ completeCallback(gif);
+ }, 0);
+ };
+
+ self.startRendering(onRenderComplete);
+ },
+ destroyWorkers: function destroyWorkers() {
+ if (this.workerError) {
+ return;
+ }
+
+ var workers = this.workers; // Explicitly ask web workers to die so they are explicitly GC'ed
+
+ utils.each(workers, function (iterator, workerObj) {
+ var worker = workerObj.worker;
+ var objectUrl = workerObj.objectUrl;
+ worker.terminate();
+ utils.URL.revokeObjectURL(objectUrl);
+ });
+ }
+ };
+ /*
+ getBase64GIF.js
+ ===============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+ function getBase64GIF(animatedGifInstance, callback) {
+ // This is asynchronous, rendered with WebWorkers
+ animatedGifInstance.getBase64GIF(function (image) {
+ callback({
+ error: false,
+ errorCode: '',
+ errorMsg: '',
+ image: image
+ });
+ });
+ }
+ /*
+ existingImages.js
+ =================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+
+ function existingImages() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var callback = obj.callback,
+ images = obj.images,
+ options = obj.options;
+ var imagesLength = obj.imagesLength;
+ var skipObj = {
+ 'getUserMedia': true,
+ 'window.URL': true
+ };
+ var errorObj = error.validate(skipObj);
+ var loadedImages = [];
+ var loadedImagesLength = 0;
+ var tempImage = void 0;
+ var ag = void 0;
+
+ if (errorObj.error) {
+ return callback(errorObj);
+ } // change workerPath to point to where Animated_GIF.worker.js is
+
+
+ ag = new AnimatedGIF(options);
+ utils.each(images, function (index, image) {
+ var currentImage = image;
+
+ if (image.src) {
+ currentImage = currentImage.src;
+ }
+
+ if (utils.isElement(currentImage)) {
+ if (options.crossOrigin) {
+ currentImage.crossOrigin = options.crossOrigin;
+ }
+
+ loadedImages[index] = currentImage;
+ loadedImagesLength += 1;
+
+ if (loadedImagesLength === imagesLength) {
+ addLoadedImagesToGif();
+ }
+ } else if (utils.isString(currentImage)) {
+ tempImage = new Image();
+
+ if (options.crossOrigin) {
+ tempImage.crossOrigin = options.crossOrigin;
+ }
+
+ (function (tempImage) {
+ if (image.text) {
+ tempImage.text = image.text;
+ }
+
+ tempImage.onerror = function (e) {
+ var obj = void 0;
+ --imagesLength; // skips over images that error out
+
+ if (imagesLength === 0) {
+ obj = {};
+ obj.error = 'None of the requested images was capable of being retrieved';
+ return callback(obj);
+ }
+ };
+
+ tempImage.onload = function (e) {
+ if (image.text) {
+ loadedImages[index] = {
+ img: tempImage,
+ text: tempImage.text
+ };
+ } else {
+ loadedImages[index] = tempImage;
+ }
+
+ loadedImagesLength += 1;
+
+ if (loadedImagesLength === imagesLength) {
+ addLoadedImagesToGif();
+ }
+
+ utils.removeElement(tempImage);
+ };
+
+ tempImage.src = currentImage;
+ })(tempImage);
+
+ utils.setCSSAttr(tempImage, {
+ position: 'fixed',
+ opacity: '0'
+ });
+ document.body.appendChild(tempImage);
+ }
+ });
+
+ function addLoadedImagesToGif() {
+ utils.each(loadedImages, function (index, loadedImage) {
+ if (loadedImage) {
+ if (loadedImage.text) {
+ ag.addFrame(loadedImage.img, options, loadedImage.text);
+ } else {
+ ag.addFrame(loadedImage, options);
+ }
+ }
+ });
+ getBase64GIF(ag, callback);
+ }
+ }
+ /*
+ screenShot.js
+ =============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+ // Helpers
+
+
+ var noop$3 = function noop() {};
+
+ var screenShot = {
+ getGIF: function getGIF() {
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var callback = arguments[1];
+ callback = utils.isFunction(callback) ? callback : noop$3;
+ var canvas = document.createElement('canvas');
+ var context = void 0;
+ var existingImages = options.images;
+ var hasExistingImages = !!existingImages.length;
+ var cameraStream = options.cameraStream,
+ crop = options.crop,
+ filter = options.filter,
+ fontColor = options.fontColor,
+ fontFamily = options.fontFamily,
+ fontWeight = options.fontWeight,
+ keepCameraOn = options.keepCameraOn,
+ numWorkers = options.numWorkers,
+ progressCallback = options.progressCallback,
+ saveRenderingContexts = options.saveRenderingContexts,
+ savedRenderingContexts = options.savedRenderingContexts,
+ text = options.text,
+ textAlign = options.textAlign,
+ textBaseline = options.textBaseline,
+ videoElement = options.videoElement,
+ videoHeight = options.videoHeight,
+ videoWidth = options.videoWidth,
+ webcamVideoElement = options.webcamVideoElement;
+ var gifWidth = Number(options.gifWidth);
+ var gifHeight = Number(options.gifHeight);
+ var interval = Number(options.interval);
+ var sampleInterval = Number(options.sampleInterval);
+ var waitBetweenFrames = hasExistingImages ? 0 : interval * 1000;
+ var renderingContextsToSave = [];
+ var numFrames = savedRenderingContexts.length ? savedRenderingContexts.length : options.numFrames;
+ var pendingFrames = numFrames;
+ var ag = new AnimatedGIF(options);
+ var fontSize = utils.getFontSize(options);
+ var textXCoordinate = options.textXCoordinate ? options.textXCoordinate : textAlign === 'left' ? 1 : textAlign === 'right' ? gifWidth : gifWidth / 2;
+ var textYCoordinate = options.textYCoordinate ? options.textYCoordinate : textBaseline === 'top' ? 1 : textBaseline === 'center' ? gifHeight / 2 : gifHeight;
+ var font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
+ var sourceX = crop ? Math.floor(crop.scaledWidth / 2) : 0;
+ var sourceWidth = crop ? videoWidth - crop.scaledWidth : 0;
+ var sourceY = crop ? Math.floor(crop.scaledHeight / 2) : 0;
+ var sourceHeight = crop ? videoHeight - crop.scaledHeight : 0;
+
+ var captureFrames = function captureSingleFrame() {
+ var framesLeft = pendingFrames - 1;
+
+ if (savedRenderingContexts.length) {
+ context.putImageData(savedRenderingContexts[numFrames - pendingFrames], 0, 0);
+ finishCapture();
+ } else {
+ drawVideo();
+ }
+
+ function drawVideo() {
+ try {
+ // Makes sure the canvas video heights/widths are in bounds
+ if (sourceWidth > videoWidth) {
+ sourceWidth = videoWidth;
+ }
+
+ if (sourceHeight > videoHeight) {
+ sourceHeight = videoHeight;
+ }
+
+ if (sourceX < 0) {
+ sourceX = 0;
+ }
+
+ if (sourceY < 0) {
+ sourceY = 0;
+ }
+
+ context.filter = filter;
+ context.drawImage(videoElement, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, gifWidth, gifHeight);
+ finishCapture();
+ } catch (e) {
+ // There is a Firefox bug that sometimes throws NS_ERROR_NOT_AVAILABLE and
+ // and IndexSizeError errors when drawing a video element to the canvas
+ if (e.name === 'NS_ERROR_NOT_AVAILABLE') {
+ // Wait 100ms before trying again
+ utils.requestTimeout(drawVideo, 100);
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ function finishCapture() {
+ var imageData = void 0;
+
+ if (saveRenderingContexts) {
+ renderingContextsToSave.push(context.getImageData(0, 0, gifWidth, gifHeight));
+ } // If there is text to display, make sure to display it on the canvas after the image is drawn
+
+
+ if (text) {
+ context.font = font;
+ context.fillStyle = fontColor;
+ context.textAlign = textAlign;
+ context.textBaseline = textBaseline;
+ context.fillText(text, textXCoordinate, textYCoordinate);
+ }
+
+ imageData = context.getImageData(0, 0, gifWidth, gifHeight);
+ ag.addFrameImageData(imageData);
+ pendingFrames = framesLeft; // Call back with an r value indicating how far along we are in capture
+
+ progressCallback((numFrames - pendingFrames) / numFrames);
+
+ if (framesLeft > 0) {
+ // test
+ utils.requestTimeout(captureSingleFrame, waitBetweenFrames);
+ }
+
+ if (!pendingFrames) {
+ ag.getBase64GIF(function (image) {
+ callback({
+ 'error': false,
+ 'errorCode': '',
+ 'errorMsg': '',
+ 'image': image,
+ 'cameraStream': cameraStream,
+ 'videoElement': videoElement,
+ 'webcamVideoElement': webcamVideoElement,
+ 'savedRenderingContexts': renderingContextsToSave,
+ 'keepCameraOn': keepCameraOn
+ });
+ });
+ }
+ }
+ };
+
+ numFrames = numFrames !== undefined ? numFrames : 10;
+ interval = interval !== undefined ? interval : 0.1; // In seconds
+
+ canvas.width = gifWidth;
+ canvas.height = gifHeight;
+ context = canvas.getContext('2d');
+
+ (function capture() {
+ if (!savedRenderingContexts.length && videoElement.currentTime === 0) {
+ utils.requestTimeout(capture, 100);
+ return;
+ }
+
+ captureFrames();
+ })();
+ },
+ getCropDimensions: function getCropDimensions() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var width = obj.videoWidth;
+ var height = obj.videoHeight;
+ var gifWidth = obj.gifWidth;
+ var gifHeight = obj.gifHeight;
+ var result = {
+ width: 0,
+ height: 0,
+ scaledWidth: 0,
+ scaledHeight: 0
+ };
+
+ if (width > height) {
+ result.width = Math.round(width * (gifHeight / height)) - gifWidth;
+ result.scaledWidth = Math.round(result.width * (height / gifHeight));
+ } else {
+ result.height = Math.round(height * (gifWidth / width)) - gifHeight;
+ result.scaledHeight = Math.round(result.height * (width / gifWidth));
+ }
+
+ return result;
+ }
+ };
+ /*
+ videoStream.js
+ ==============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+ var videoStream = {
+ loadedData: false,
+ defaultVideoDimensions: {
+ width: 640,
+ height: 480
+ },
+ findVideoSize: function findVideoSizeMethod(obj) {
+ findVideoSizeMethod.attempts = findVideoSizeMethod.attempts || 0;
+ var cameraStream = obj.cameraStream,
+ completedCallback = obj.completedCallback,
+ videoElement = obj.videoElement;
+
+ if (!videoElement) {
+ return;
+ }
+
+ if (videoElement.videoWidth > 0 && videoElement.videoHeight > 0) {
+ videoElement.removeEventListener('loadeddata', videoStream.findVideoSize);
+ completedCallback({
+ videoElement: videoElement,
+ cameraStream: cameraStream,
+ videoWidth: videoElement.videoWidth,
+ videoHeight: videoElement.videoHeight
+ });
+ } else {
+ if (findVideoSizeMethod.attempts < 10) {
+ findVideoSizeMethod.attempts += 1;
+ utils.requestTimeout(function () {
+ videoStream.findVideoSize(obj);
+ }, 400);
+ } else {
+ completedCallback({
+ videoElement: videoElement,
+ cameraStream: cameraStream,
+ videoWidth: videoStream.defaultVideoDimensions.width,
+ videoHeight: videoStream.defaultVideoDimensions.height
+ });
+ }
+ }
+ },
+ onStreamingTimeout: function onStreamingTimeout(callback) {
+ if (utils.isFunction(callback)) {
+ callback({
+ error: true,
+ errorCode: 'getUserMedia',
+ errorMsg: 'There was an issue with the getUserMedia API - Timed out while trying to start streaming',
+ image: null,
+ cameraStream: {}
+ });
+ }
+ },
+ stream: function stream(obj) {
+ var existingVideo = utils.isArray(obj.existingVideo) ? obj.existingVideo[0] : obj.existingVideo;
+ var cameraStream = obj.cameraStream,
+ completedCallback = obj.completedCallback,
+ streamedCallback = obj.streamedCallback,
+ videoElement = obj.videoElement;
+
+ if (utils.isFunction(streamedCallback)) {
+ streamedCallback();
+ }
+
+ if (existingVideo) {
+ if (utils.isString(existingVideo)) {
+ videoElement.src = existingVideo;
+ videoElement.innerHTML = '';
+ } else if (existingVideo instanceof Blob) {
+ try {
+ videoElement.src = utils.URL.createObjectURL(existingVideo);
+ } catch (e) {}
+
+ videoElement.innerHTML = '';
+ }
+ } else if (videoElement.mozSrcObject) {
+ videoElement.mozSrcObject = cameraStream;
+ } else if (utils.URL) {
+ try {
+ videoElement.srcObject = cameraStream;
+ videoElement.src = utils.URL.createObjectURL(cameraStream);
+ } catch (e) {
+ videoElement.srcObject = cameraStream;
+ }
+ }
+
+ videoElement.play();
+ utils.requestTimeout(function checkLoadedData() {
+ checkLoadedData.count = checkLoadedData.count || 0;
+
+ if (videoStream.loadedData === true) {
+ videoStream.findVideoSize({
+ videoElement: videoElement,
+ cameraStream: cameraStream,
+ completedCallback: completedCallback
+ });
+ videoStream.loadedData = false;
+ } else {
+ checkLoadedData.count += 1;
+
+ if (checkLoadedData.count > 10) {
+ videoStream.findVideoSize({
+ videoElement: videoElement,
+ cameraStream: cameraStream,
+ completedCallback: completedCallback
+ });
+ } else {
+ checkLoadedData();
+ }
+ }
+ }, 0);
+ },
+ startStreaming: function startStreaming(obj) {
+ var errorCallback = utils.isFunction(obj.error) ? obj.error : utils.noop;
+ var streamedCallback = utils.isFunction(obj.streamed) ? obj.streamed : utils.noop;
+ var completedCallback = utils.isFunction(obj.completed) ? obj.completed : utils.noop;
+ var crossOrigin = obj.crossOrigin,
+ existingVideo = obj.existingVideo,
+ lastCameraStream = obj.lastCameraStream,
+ options = obj.options,
+ webcamVideoElement = obj.webcamVideoElement;
+ var videoElement = utils.isElement(existingVideo) ? existingVideo : webcamVideoElement ? webcamVideoElement : document.createElement('video');
+
+ if (crossOrigin) {
+ videoElement.crossOrigin = options.crossOrigin;
+ }
+
+ videoElement.autoplay = true;
+ videoElement.loop = true;
+ videoElement.muted = true;
+ videoElement.addEventListener('loadeddata', function (event) {
+ videoStream.loadedData = true;
+
+ if (options.offset) {
+ videoElement.currentTime = options.offset;
+ }
+ });
+
+ if (existingVideo) {
+ videoStream.stream({
+ videoElement: videoElement,
+ existingVideo: existingVideo,
+ completedCallback: completedCallback
+ });
+ } else if (lastCameraStream) {
+ videoStream.stream({
+ videoElement: videoElement,
+ cameraStream: lastCameraStream,
+ streamedCallback: streamedCallback,
+ completedCallback: completedCallback
+ });
+ } else {
+ utils.getUserMedia({
+ video: true
+ }, function (stream) {
+ videoStream.stream({
+ videoElement: videoElement,
+ cameraStream: stream,
+ streamedCallback: streamedCallback,
+ completedCallback: completedCallback
+ });
+ }, errorCallback);
+ }
+ },
+ startVideoStreaming: function startVideoStreaming(callback) {
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ var timeoutLength = options.timeout !== undefined ? options.timeout : 0;
+ var originalCallback = options.callback;
+ var webcamVideoElement = options.webcamVideoElement;
+ var noGetUserMediaSupportTimeout = void 0; // Some browsers apparently have support for video streaming because of the
+ // presence of the getUserMedia function, but then do not answer our
+ // calls for streaming.
+ // So we'll set up this timeout and if nothing happens after a while, we'll
+ // conclude that there's no actual getUserMedia support.
+
+ if (timeoutLength > 0) {
+ noGetUserMediaSupportTimeout = utils.requestTimeout(function () {
+ videoStream.onStreamingTimeout(originalCallback);
+ }, 10000);
+ }
+
+ videoStream.startStreaming({
+ error: function error() {
+ originalCallback({
+ error: true,
+ errorCode: 'getUserMedia',
+ errorMsg: 'There was an issue with the getUserMedia API - the user probably denied permission',
+ image: null,
+ cameraStream: {}
+ });
+ },
+ streamed: function streamed() {
+ // The streaming started somehow, so we can assume there is getUserMedia support
+ clearTimeout(noGetUserMediaSupportTimeout);
+ },
+ completed: function completed() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var cameraStream = obj.cameraStream,
+ videoElement = obj.videoElement,
+ videoHeight = obj.videoHeight,
+ videoWidth = obj.videoWidth;
+ callback({
+ cameraStream: cameraStream,
+ videoElement: videoElement,
+ videoHeight: videoHeight,
+ videoWidth: videoWidth
+ });
+ },
+ lastCameraStream: options.lastCameraStream,
+ webcamVideoElement: webcamVideoElement,
+ crossOrigin: options.crossOrigin,
+ options: options
+ });
+ },
+ stopVideoStreaming: function stopVideoStreaming(obj) {
+ obj = utils.isObject(obj) ? obj : {};
+ var _obj = obj,
+ keepCameraOn = _obj.keepCameraOn,
+ videoElement = _obj.videoElement,
+ webcamVideoElement = _obj.webcamVideoElement;
+ var cameraStream = obj.cameraStream || {};
+ var cameraStreamTracks = cameraStream.getTracks ? cameraStream.getTracks() || [] : [];
+ var hasCameraStreamTracks = !!cameraStreamTracks.length;
+ var firstCameraStreamTrack = cameraStreamTracks[0];
+
+ if (!keepCameraOn && hasCameraStreamTracks) {
+ if (utils.isFunction(firstCameraStreamTrack.stop)) {
+ // Stops the camera stream
+ firstCameraStreamTrack.stop();
+ }
+ }
+
+ if (utils.isElement(videoElement) && !webcamVideoElement) {
+ // Pauses the video, revokes the object URL (freeing up memory), and remove the video element
+ videoElement.pause(); // Destroys the object url
+
+ if (utils.isFunction(utils.URL.revokeObjectURL) && !utils.webWorkerError) {
+ if (videoElement.src) {
+ utils.URL.revokeObjectURL(videoElement.src);
+ }
+ } // Removes the video element from the DOM
+
+
+ utils.removeElement(videoElement);
+ }
+ }
+ };
+ /*
+ stopVideoStreaming.js
+ =====================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+ function stopVideoStreaming(options) {
+ options = utils.isObject(options) ? options : {};
+ videoStream.stopVideoStreaming(options);
+ }
+ /*
+ createAndGetGIF.js
+ ==================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function createAndGetGIF(obj, callback) {
+ var options = obj.options || {};
+ var images = options.images,
+ video = options.video;
+ var gifWidth = Number(options.gifWidth);
+ var gifHeight = Number(options.gifHeight);
+ var numFrames = Number(options.numFrames);
+ var cameraStream = obj.cameraStream,
+ videoElement = obj.videoElement,
+ videoWidth = obj.videoWidth,
+ videoHeight = obj.videoHeight;
+ var cropDimensions = screenShot.getCropDimensions({
+ videoWidth: videoWidth,
+ videoHeight: videoHeight,
+ gifHeight: gifHeight,
+ gifWidth: gifWidth
+ });
+ var completeCallback = callback;
+ options.crop = cropDimensions;
+ options.videoElement = videoElement;
+ options.videoWidth = videoWidth;
+ options.videoHeight = videoHeight;
+ options.cameraStream = cameraStream;
+
+ if (!utils.isElement(videoElement)) {
+ return;
+ }
+
+ videoElement.width = gifWidth + cropDimensions.width;
+ videoElement.height = gifHeight + cropDimensions.height;
+
+ if (!options.webcamVideoElement) {
+ utils.setCSSAttr(videoElement, {
+ position: 'fixed',
+ opacity: '0'
+ });
+ document.body.appendChild(videoElement);
+ } // Firefox doesn't seem to obey autoplay if the element is not in the DOM when the content
+ // is loaded, so we must manually trigger play after adding it, or the video will be frozen
+
+
+ videoElement.play();
+ screenShot.getGIF(options, function (obj) {
+ if ((!images || !images.length) && (!video || !video.length)) {
+ stopVideoStreaming(obj);
+ }
+
+ completeCallback(obj);
+ });
+ }
+ /*
+ existingVideo.js
+ ================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function existingVideo() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var callback = obj.callback,
+ existingVideo = obj.existingVideo,
+ options = obj.options;
+ var skipObj = {
+ getUserMedia: true,
+ 'window.URL': true
+ };
+ var errorObj = error.validate(skipObj);
+ var videoType = void 0;
+ var videoSrc = void 0;
+
+ if (errorObj.error) {
+ return callback(errorObj);
+ }
+
+ if (utils.isElement(existingVideo) && existingVideo.src) {
+ videoSrc = existingVideo.src;
+ videoType = utils.getExtension(videoSrc);
+
+ if (!utils.isSupported.videoCodecs[videoType]) {
+ return callback(error.messages.videoCodecs);
+ }
+ } else if (utils.isArray(existingVideo)) {
+ utils.each(existingVideo, function (iterator, videoSrc) {
+ if (videoSrc instanceof Blob) {
+ videoType = videoSrc.type.substr(videoSrc.type.lastIndexOf('/') + 1, videoSrc.length);
+ } else {
+ videoType = videoSrc.substr(videoSrc.lastIndexOf('.') + 1, videoSrc.length);
+ }
+
+ if (utils.isSupported.videoCodecs[videoType]) {
+ existingVideo = videoSrc;
+ return false;
+ }
+ });
+ }
+
+ videoStream.startStreaming({
+ completed: function completed(obj) {
+ obj.options = options || {};
+ createAndGetGIF(obj, callback);
+ },
+ existingVideo: existingVideo,
+ crossOrigin: options.crossOrigin,
+ options: options
+ });
+ }
+ /*
+ existingWebcam.js
+ =================
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function existingWebcam() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var callback = obj.callback,
+ lastCameraStream = obj.lastCameraStream,
+ options = obj.options,
+ webcamVideoElement = obj.webcamVideoElement;
+
+ if (!isWebCamGIFSupported()) {
+ return callback(error.validate());
+ }
+
+ if (options.savedRenderingContexts.length) {
+ screenShot.getGIF(options, function (obj) {
+ callback(obj);
+ });
+ return;
+ }
+
+ videoStream.startVideoStreaming(function () {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ obj.options = options || {};
+ createAndGetGIF(obj, callback);
+ }, {
+ lastCameraStream: lastCameraStream,
+ callback: callback,
+ webcamVideoElement: webcamVideoElement,
+ crossOrigin: options.crossOrigin
+ });
+ }
+ /*
+ createGIF.js
+ ============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ function createGIF(userOptions, callback) {
+ callback = utils.isFunction(userOptions) ? userOptions : callback;
+ userOptions = utils.isObject(userOptions) ? userOptions : {};
+
+ if (!utils.isFunction(callback)) {
+ return;
+ }
+
+ var options = utils.mergeOptions(defaultOptions, userOptions) || {};
+ var lastCameraStream = userOptions.cameraStream;
+ var images = options.images;
+ var imagesLength = images ? images.length : 0;
+ var video = options.video;
+ var webcamVideoElement = options.webcamVideoElement;
+ options = utils.mergeOptions(options, {
+ 'gifWidth': Math.floor(options.gifWidth),
+ 'gifHeight': Math.floor(options.gifHeight)
+ }); // If the user would like to create a GIF from an existing image(s)
+
+ if (imagesLength) {
+ existingImages({
+ 'images': images,
+ 'imagesLength': imagesLength,
+ 'callback': callback,
+ 'options': options
+ });
+ } else if (video) {
+ // If the user would like to create a GIF from an existing HTML5 video
+ existingVideo({
+ 'existingVideo': video,
+ callback: callback,
+ options: options
+ });
+ } else {
+ // If the user would like to create a GIF from a webcam stream
+ existingWebcam({
+ lastCameraStream: lastCameraStream,
+ callback: callback,
+ webcamVideoElement: webcamVideoElement,
+ options: options
+ });
+ }
+ }
+ /*
+ takeSnapShot.js
+ ===============
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+
+
+ function takeSnapShot(userOptions, callback) {
+ callback = utils.isFunction(userOptions) ? userOptions : callback;
+ userOptions = utils.isObject(userOptions) ? userOptions : {};
+
+ if (!utils.isFunction(callback)) {
+ return;
+ }
+
+ var mergedOptions = utils.mergeOptions(defaultOptions, userOptions);
+ var options = utils.mergeOptions(mergedOptions, {
+ 'interval': .1,
+ 'numFrames': 1,
+ 'gifWidth': Math.floor(mergedOptions.gifWidth),
+ 'gifHeight': Math.floor(mergedOptions.gifHeight)
+ });
+ createGIF(options, callback);
+ }
+ /*
+ API.js
+ ======
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Dependencies
+
+
+ var API = {
+ 'utils': utils$2,
+ 'error': error$2,
+ 'defaultOptions': defaultOptions$2,
+ 'createGIF': createGIF,
+ 'takeSnapShot': takeSnapShot,
+ 'stopVideoStreaming': stopVideoStreaming,
+ 'isSupported': isSupported,
+ 'isWebCamGIFSupported': isWebCamGIFSupported,
+ 'isExistingVideoGIFSupported': isExistingVideoGIFSupported,
+ 'isExistingImagesGIFSupported': isSupported$1,
+ 'VERSION': '0.4.5'
+ };
+ /*
+ index.js
+ ========
+ */
+
+ /* Copyright 2017 Yahoo Inc.
+ * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
+ */
+ // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, and plain browser loading
+
+ if (typeof undefined === 'function' && undefined.amd) {
+ undefined([], function () {
+ return API;
+ });
+ } else {
+ module.exports = API;
+ }
+ })(typeof window !== "undefined" ? window : {}, typeof document !== "undefined" ? document : {
+ createElement: function createElement() {}
+ }, typeof window !== "undefined" ? window.navigator : {});
+ });
+
+ function i18nMix(i18n) {
+ i18n.update({
+ 'zh-cn': {
+ 'Long press, gif length is between 1 second and 5 seconds': '长按,gif 长度为 1 ~ 5 秒',
+ 'Gif time is too short': 'Gif 时间太短',
+ 'Start creating gif, please wait': '开始创建 gif,请稍等',
+ 'Create gif successfully': '创建 gif 成功',
+ 'There is another gif in the processing': '正有另一个 gif 在创建中'
+ },
+ 'zh-tw': {
+ 'Long press, gif length is between 1 second and 5 seconds': '長按,gif 長度為 1 ~ 5 秒',
+ 'Gif time is too short': 'Gif 時間太短',
+ 'Start creating gif, please wait': '開始創建 gif,請稍等',
+ 'Create gif successfully': '創建 gif 成功',
+ 'There is another gif in the processing': '正有另一個 gif 在創建中'
+ }
+ });
+ }
+
+ function artplayerPluginGif(art) {
+ var _art$constructor$util = art.constructor.utils,
+ errorHandle = _art$constructor$util.errorHandle,
+ clamp = _art$constructor$util.clamp,
+ downloadImage = _art$constructor$util.downloadImage;
+ var i18n = art.i18n,
+ notice = art.notice,
+ layers = art.layers,
+ controls = art.controls,
+ player = art.player,
+ _art$option = art.option,
+ theme = _art$option.theme,
+ title = _art$option.title,
+ proxy = art.events.proxy,
+ $video = art.template.$video;
+ i18nMix(i18n);
+ layers.add({
+ name: 'artplayer-plugin-gif-progress',
+ html: '',
+ style: {
+ position: 'absolute',
+ top: '0',
+ left: '0',
+ height: '3px',
+ width: '0%',
+ 'background-color': theme
+ }
+ });
+ var $progress = layers['artplayer-plugin-gif-progress'].$ref;
+ var isProcessing = false;
+ var pressStartTime = 0;
+ var progressTimer = null;
+
+ function cleanTimer() {
+ $progress.style.width = '0%';
+ clearTimeout(progressTimer);
+ progressTimer = null;
+ }
+
+ function createGif() {
+ cleanTimer();
+ var pressTime = new Date() - pressStartTime;
+
+ if (isProcessing) {
+ notice.show(i18n.get('There is another gif in the processing'));
+ } else if (pressTime < 1000) {
+ notice.show(i18n.get('Gif time is too short'));
+ } else {
+ var numFrames = Math.floor(clamp(pressTime, 1000, 5000) / 100);
+ var videoWidth = $video.videoWidth,
+ videoHeight = $video.videoHeight;
+ art.plugins.artplayerPluginGif.create({
+ numFrames: numFrames,
+ gifHeight: 200,
+ gifWidth: videoWidth / videoHeight * 200
+ }, function (image) {
+ downloadImage(image, "".concat(title || 'unnamed', ".gif"));
+ });
+ }
+ }
+
+ controls.add({
+ name: 'artplayer-plugin-gif',
+ position: 'right',
+ html: 'GIF',
+ mounted: function mounted($gif) {
+ proxy($gif, 'mousedown', function () {
+ cleanTimer();
+ pressStartTime = new Date();
+ notice.show(i18n.get('Long press, gif length is between 1 second and 5 seconds'));
+
+ (function loop() {
+ progressTimer = setTimeout(function () {
+ var width = parseInt($progress.style.width, 10);
+
+ if (width <= 100) {
+ $progress.style.width = "".concat(width + 1, "%");
+ loop();
+ } else {
+ createGif();
+ }
+ }, 50);
+ })();
+ });
+ proxy($gif, 'mouseup', function () {
+ createGif();
+ });
+ }
+ });
+ return {
+ name: 'artplayerPluginGif',
+ isProcessing: isProcessing,
+ create: function create() {
+ var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var callback = arguments.length > 1 ? arguments[1] : undefined;
+ isProcessing = true;
+ art.emit('artplayerPluginGif:create:start');
+ notice.show("".concat(i18n.get('Start creating gif, please wait'), ": ").concat(config.numFrames / 10 || 1, " s"), false, 5000);
+ gifshot.createGIF(objectSpread({}, config, {
+ offset: player.currentTime,
+ video: [$video.src]
+ }), function (obj) {
+ if (obj.error) {
+ notice.show(obj.errorMsg);
+ errorHandle(false, obj.errorMsg);
+ } else if (typeof callback === 'function') {
+ callback(obj.image);
+ notice.show(i18n.get('Create gif successfully'));
+ }
+
+ isProcessing = false;
+ art.emit('artplayerPluginGif:create:end');
+ });
+ }
+ };
+ }
+
+ window.artplayerPluginGif = artplayerPluginGif;
+
+ exports.default = artplayerPluginGif;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
diff --git a/docs/uncompiled/artplayer.css b/docs/uncompiled/artplayer.css
index 7eb9eb32c..48cb2939b 100644
--- a/docs/uncompiled/artplayer.css
+++ b/docs/uncompiled/artplayer.css
@@ -1,18 +1,18 @@
-.artplayer-video-player .artplayer-video{position:absolute;z-index:10;left:0;top:0;right:0;bottom:0;width:100%;height:100%;background-color:#000;outline:0;cursor:pointer}
.artplayer-video-player .artplayer-subtitle{position:absolute;z-index:20;bottom:40px;width:100%;padding:0 20px;text-align:center;color:#fff;text-shadow:.5px .5px .5px rgba(0,0,0,.5);font-size:20px;pointer-events:none}.artplayer-video-player .artplayer-subtitle p{margin:5px 0}
+.artplayer-video-player .artplayer-video{position:absolute;z-index:10;left:0;top:0;right:0;bottom:0;width:100%;height:100%;background-color:#000;outline:0;cursor:pointer}
.artplayer-video-player{position:relative;width:100%;height:100%;overflow:hidden;outline:0;font-family:Roboto,Arial,Helvetica,sans-serif;color:#eee;text-align:left;direction:ltr;font-size:14px;line-height:1.3;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0);touch-action:manipulation;-ms-high-contrast-adjust:none}.artplayer-video-player *{box-sizing:border-box}.artplayer-video-player .art-icon{display:inline-block;line-height:1.5}.artplayer-video-player .art-icon svg{fill:#fff}
.artplayer-video-player .artplayer-layers{position:absolute;z-index:30;left:0;top:0;right:0;bottom:0;width:100%;height:100%;pointer-events:none}.artplayer-video-player .artplayer-layers .art-layer{pointer-events:all}
-.artplayer-video-player .artplayer-loading{display:none;position:absolute;z-index:60;left:0;top:0;right:0;bottom:0;width:100%;height:100%;align-items:center;justify-content:center;pointer-events:none}
.artplayer-video-player .artplayer-mask{display:none;position:absolute;z-index:40;left:0;top:0;right:0;bottom:0;width:100%;height:100%;pointer-events:none}.artplayer-video-player .artplayer-mask .art-playBig{position:absolute;right:30px;bottom:65px;opacity:.6}
-.artplayer-video-player .artplayer-bottom{position:absolute;z-index:50;left:0;right:0;bottom:0;height:100px;padding:60px 10px 0;opacity:0;visibility:hidden;transition:all .2s ease-in-out;pointer-events:none;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==) repeat-x bottom}.artplayer-video-player .artplayer-bottom .artplayer-progress{position:relative;pointer-events:all}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress{position:relative;height:5px;padding:1px 0;cursor:pointer}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner{position:relative;height:100%;background:hsla(0,0%,100%,.2)}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-loaded{position:absolute;z-index:10;left:0;top:0;right:0;bottom:0;height:100%;width:0;background:hsla(0,0%,100%,.4)}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-played{position:absolute;z-index:20;left:0;top:0;right:0;bottom:0;height:100%;width:0}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-highlight{position:absolute;z-index:30;left:0;top:0;right:0;bottom:0;height:0}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-highlight span{display:inline-block;position:absolute;left:0;top:0;width:7px;height:3px;background:#fff;border-radius:3px}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-indicator{display:none;position:absolute;z-index:40;top:-5px;left:-6.5px;width:13px;height:13px;border-radius:50%}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-indicator.art-show-indicator{display:block}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-tip{display:none;position:absolute;z-index:50;top:-25px;left:0;height:20px;padding:0 5px;line-height:20px;color:#fff;font-size:12px;text-align:center;background:rgba(0,0,0,.7);border-radius:3px;font-weight:700}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress:hover .art-progress-indicator,.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress:hover .art-progress-tip{display:block}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-thumbnails{display:none;position:absolute;bottom:8px;left:0;pointer-events:none;background-color:rgba(0,0,0,.7)}.artplayer-video-player .artplayer-bottom .artplayer-controls{position:relative;pointer-events:all;display:flex;align-items:center;justify-content:space-between;height:36px}.artplayer-video-player .artplayer-bottom .artplayer-controls .artplayer-controls-left,.artplayer-video-player .artplayer-bottom .artplayer-controls .artplayer-controls-right{display:flex}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control{opacity:.9;font-size:12px;height:36px;line-height:36px;cursor:pointer}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control .art-icon{display:flex;align-items:center;justify-content:center;float:left;height:36px;width:36px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control:hover{opacity:1}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel{position:relative;float:left;width:0;height:100%;transition:margin .2s cubic-bezier(.4,0,1,1),width .2s cubic-bezier(.4,0,1,1);overflow:hidden}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel .art-volume-slider-handle{position:absolute;top:50%;left:0;width:12px;height:12px;border-radius:12px;margin-top:-6px;background:#fff}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel .art-volume-slider-handle:before{left:-54px;background:#fff}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel .art-volume-slider-handle:after{left:6px;background:hsla(0,0%,100%,.2)}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel .art-volume-slider-handle:after,.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel .art-volume-slider-handle:before{content:"";position:absolute;display:block;top:50%;height:3px;margin-top:-2px;width:60px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume:hover .art-volume-panel{width:60px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-time{padding:0 10px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-quality{position:relative;z-index:30;padding:0 10px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-quality .art-qualitys{display:none;position:absolute;left:-15px;bottom:35px;width:100px;padding:5px 0;text-align:center;color:#fff;background:rgba(0,0,0,.7);border-radius:3px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-quality .art-qualitys .art-quality-item{height:30px;line-height:30px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-shadow:0 0 2px rgba(0,0,0,.5)}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-quality .art-qualitys .art-quality-item:hover{background-color:hsla(0,0%,100%,.1)}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-quality:hover .art-qualitys{display:block}.artplayer-video-player.artplayer-controls-show .artplayer-bottom,.artplayer-video-player.artplayer-hover .artplayer-bottom{opacity:1;visibility:visible}.artplayer-video-player.artplayer-destroy .art-progress-indicator,.artplayer-video-player.artplayer-destroy .art-progress-tip,.artplayer-video-player.artplayer-error .art-progress-indicator,.artplayer-video-player.artplayer-error .art-progress-tip{display:none!important}
-.artplayer-video-player .artplayer-contextmenu{display:none;position:absolute;z-index:110;left:0;top:0;min-width:200px;padding:5px 0;background:rgba(0,0,0,.8);border-radius:3px}.artplayer-video-player .artplayer-contextmenu .art-contextmenu{cursor:pointer;font-size:13px;display:block;color:#fff;padding:7px 10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-shadow:0 0 2px rgba(0,0,0,.5);border-bottom:1px solid hsla(0,0%,100%,.1)}.artplayer-video-player .artplayer-contextmenu .art-contextmenu a{color:#fff;text-decoration:none}.artplayer-video-player .artplayer-contextmenu .art-contextmenu span{display:inline-block;padding:0 7px}.artplayer-video-player .artplayer-contextmenu .art-contextmenu span.current,.artplayer-video-player .artplayer-contextmenu .art-contextmenu span:hover{color:#00c9ff}.artplayer-video-player .artplayer-contextmenu .art-contextmenu:hover{background-color:hsla(0,0%,100%,.1)}.artplayer-video-player .artplayer-contextmenu .art-contextmenu:last-child{border-bottom:none}
+.artplayer-video-player .artplayer-loading{display:none;position:absolute;z-index:60;left:0;top:0;right:0;bottom:0;width:100%;height:100%;align-items:center;justify-content:center;pointer-events:none}
+.artplayer-video-player .artplayer-bottom{position:absolute;z-index:50;left:0;right:0;bottom:0;height:100px;padding:60px 10px 0;opacity:0;visibility:hidden;transition:all .2s ease-in-out;pointer-events:none;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==) repeat-x bottom}.artplayer-video-player .artplayer-bottom .artplayer-progress{position:relative;pointer-events:all}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress{position:relative;height:5px;padding:1px 0;cursor:pointer}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner{position:relative;height:100%;background:hsla(0,0%,100%,.2)}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-loaded{position:absolute;z-index:10;left:0;top:0;right:0;bottom:0;height:100%;width:0;background:hsla(0,0%,100%,.4)}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-played{position:absolute;z-index:20;left:0;top:0;right:0;bottom:0;height:100%;width:0}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-highlight{position:absolute;z-index:30;left:0;top:0;right:0;bottom:0;height:0}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-highlight span{display:inline-block;position:absolute;left:0;top:0;width:7px;height:3px;background:#fff;border-radius:3px}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-indicator{display:none;position:absolute;z-index:40;top:-5px;left:-6.5px;width:13px;height:13px;border-radius:50%}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-indicator.art-show-indicator{display:block}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress .art-control-progress-inner .art-progress-tip{display:none;position:absolute;z-index:50;top:-25px;left:0;height:20px;padding:0 5px;line-height:20px;color:#fff;font-size:12px;text-align:center;background:rgba(0,0,0,.7);border-radius:3px;font-weight:700}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress:hover .art-progress-indicator,.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-progress:hover .art-progress-tip{display:block}.artplayer-video-player .artplayer-bottom .artplayer-progress .art-control-thumbnails{display:none;position:absolute;bottom:8px;left:0;pointer-events:none;background-color:rgba(0,0,0,.7)}.artplayer-video-player .artplayer-bottom .artplayer-controls{position:relative;pointer-events:all;display:flex;align-items:center;justify-content:space-between;height:36px}.artplayer-video-player .artplayer-bottom .artplayer-controls .artplayer-controls-left,.artplayer-video-player .artplayer-bottom .artplayer-controls .artplayer-controls-right{display:flex}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control{opacity:.9;font-size:12px;height:36px;min-width:36px;line-height:36px;text-align:center;cursor:pointer}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control .art-icon{display:flex;align-items:center;justify-content:center;float:left;height:36px;width:36px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control:hover{opacity:1}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel{position:relative;float:left;width:0;height:100%;transition:margin .2s cubic-bezier(.4,0,1,1),width .2s cubic-bezier(.4,0,1,1);overflow:hidden}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel .art-volume-slider-handle{position:absolute;top:50%;left:0;width:12px;height:12px;border-radius:12px;margin-top:-6px;background:#fff}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel .art-volume-slider-handle:before{left:-54px;background:#fff}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel .art-volume-slider-handle:after{left:6px;background:hsla(0,0%,100%,.2)}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel .art-volume-slider-handle:after,.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume .art-volume-panel .art-volume-slider-handle:before{content:"";position:absolute;display:block;top:50%;height:3px;margin-top:-2px;width:60px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-volume:hover .art-volume-panel{width:60px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-time{padding:0 10px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-quality{position:relative;z-index:30;padding:0 10px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-quality .art-qualitys{display:none;position:absolute;left:-15px;bottom:35px;width:100px;padding:5px 0;text-align:center;color:#fff;background:rgba(0,0,0,.7);border-radius:3px}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-quality .art-qualitys .art-quality-item{height:30px;line-height:30px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-shadow:0 0 2px rgba(0,0,0,.5)}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-quality .art-qualitys .art-quality-item:hover{background-color:hsla(0,0%,100%,.1)}.artplayer-video-player .artplayer-bottom .artplayer-controls .art-control-quality:hover .art-qualitys{display:block}.artplayer-video-player.artplayer-controls-show .artplayer-bottom,.artplayer-video-player.artplayer-hover .artplayer-bottom{opacity:1;visibility:visible}.artplayer-video-player.artplayer-destroy .art-progress-indicator,.artplayer-video-player.artplayer-destroy .art-progress-tip,.artplayer-video-player.artplayer-error .art-progress-indicator,.artplayer-video-player.artplayer-error .art-progress-tip{display:none!important}
.artplayer-video-player .artplayer-notice{display:none;font-size:14px;position:absolute;z-index:70;left:0;top:0;padding:10px;width:100%;pointer-events:none}.artplayer-video-player .artplayer-notice .artplayer-notice-inner{display:inline-block;padding:5px 10px;color:#fff;background:rgba(0,0,0,.6);border-radius:3px}
-.artplayer-video-player .artplayer-info{display:none;position:absolute;left:10px;top:10px;z-index:90;width:350px;min-height:150px;padding:10px;color:#fff;font-size:12px;font-family:Noto Sans CJK SC DemiLight,Roboto,Segoe UI,Tahoma,Arial,Helvetica,sans-serif;-webkit-font-smoothing:antialiased;background:rgba(0,0,0,.7)}.artplayer-video-player .artplayer-info .art-info-item{display:flex;margin-bottom:5px}.artplayer-video-player .artplayer-info .art-info-item .art-info-title{width:100px;text-align:right}.artplayer-video-player .artplayer-info .art-info-item .art-info-content{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:5px}.artplayer-video-player .artplayer-info .artplayer-info-close{position:absolute;top:5px;right:5px;cursor:pointer}
+.artplayer-video-player .artplayer-contextmenu{display:none;position:absolute;z-index:110;left:0;top:0;min-width:200px;padding:5px 0;background:rgba(0,0,0,.8);border-radius:3px}.artplayer-video-player .artplayer-contextmenu .art-contextmenu{cursor:pointer;font-size:13px;display:block;color:#fff;padding:7px 10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-shadow:0 0 2px rgba(0,0,0,.5);border-bottom:1px solid hsla(0,0%,100%,.1)}.artplayer-video-player .artplayer-contextmenu .art-contextmenu a{color:#fff;text-decoration:none}.artplayer-video-player .artplayer-contextmenu .art-contextmenu span{display:inline-block;padding:0 7px}.artplayer-video-player .artplayer-contextmenu .art-contextmenu span.current,.artplayer-video-player .artplayer-contextmenu .art-contextmenu span:hover{color:#00c9ff}.artplayer-video-player .artplayer-contextmenu .art-contextmenu:hover{background-color:hsla(0,0%,100%,.1)}.artplayer-video-player .artplayer-contextmenu .art-contextmenu:last-child{border-bottom:none}
.artplayer-video-player.artplayer-hide-cursor *{cursor:none!important}
.artplayer-video-player .artplayer-setting{display:none;position:absolute;z-index:80;left:0;top:0;height:100%;width:100%;align-items:center;justify-content:center;pointer-events:none}.artplayer-video-player .artplayer-setting .artplayer-setting-inner{position:relative;width:300px;height:250px;font-size:12px;background:rgba(0,0,0,.8);border-radius:3px;pointer-events:all;overflow:hidden}.artplayer-video-player .artplayer-setting .artplayer-setting-inner .artplayer-setting-body{overflow-y:auto;width:100%;height:100%}.artplayer-video-player .artplayer-setting .artplayer-setting-inner .artplayer-setting-body .art-setting{border-bottom:1px solid hsla(0,0%,100%,.1);padding:12px 20px}.artplayer-video-player .artplayer-setting .artplayer-setting-inner .artplayer-setting-body .art-setting .art-setting-header{color:#fff;margin-bottom:10px}.artplayer-video-player .artplayer-setting .artplayer-setting-inner .artplayer-setting-body .art-setting .art-setting-body{color:hsla(0,0%,100%,.8)}.artplayer-video-player .artplayer-setting .artplayer-setting-inner .artplayer-setting-close{position:absolute;top:0;right:0;width:20px;height:20px;line-height:20px;text-align:center;color:hsla(0,0%,100%,.8);background:rgba(0,0,0,.5);font-size:20px;cursor:pointer}.artplayer-video-player .artplayer-setting .art-setting-btns{display:flex;margin-left:-4px;margin-right:-4px}.artplayer-video-player .artplayer-setting .art-setting-btns .art-setting-btn{flex:1;padding:0 4px}.artplayer-video-player .artplayer-setting .art-setting-btns .art-setting-btn span{display:block;height:20px;line-height:20px;text-align:center;color:#fff;background:hsla(0,0%,100%,.3);border-radius:2px;cursor:pointer}.artplayer-video-player .artplayer-setting .art-setting-btns .art-setting-btn.current span,.artplayer-video-player .artplayer-setting .art-setting-btns .art-setting-btn:hover span{color:#fff;background:#00a1d6}
+.artplayer-video-player .artplayer-info{display:none;position:absolute;left:10px;top:10px;z-index:90;width:350px;min-height:150px;padding:10px;color:#fff;font-size:12px;font-family:Noto Sans CJK SC DemiLight,Roboto,Segoe UI,Tahoma,Arial,Helvetica,sans-serif;-webkit-font-smoothing:antialiased;background:rgba(0,0,0,.7)}.artplayer-video-player .artplayer-info .art-info-item{display:flex;margin-bottom:5px}.artplayer-video-player .artplayer-info .art-info-item .art-info-title{width:100px;text-align:right}.artplayer-video-player .artplayer-info .art-info-item .art-info-content{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:5px}.artplayer-video-player .artplayer-info .artplayer-info-close{position:absolute;top:5px;right:5px;cursor:pointer}
+.artplayer-video-player.artplayer-web-fullscreen{position:fixed;z-index:9999;width:100%;height:100%;left:0;top:0;right:0;bottom:0}
.artplayer-video-player[data-aspect-ratio] video{box-sizing:content-box;-o-object-fit:fill;object-fit:fill}
.artplayer-video-player .artplayer-pip-header{display:none;position:absolute;z-index:100;left:0;top:0;right:0;height:35px;line-height:35px;color:#fff;background:rgba(0,0,0,.5);align-items:center;justify-content:space-between;opacity:0;visibility:hidden;transition:all .2s ease-in-out}.artplayer-video-player .artplayer-pip-header .artplayer-pip-title{flex:1;padding:0 10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:move}.artplayer-video-player .artplayer-pip-header .artplayer-pip-close{width:35px;text-align:center;font-size:22px;cursor:pointer}.artplayer-video-player.is-dragging{opacity:.7}.artplayer-video-player.artplayer-pip{position:fixed;z-index:9999;right:20px;bottom:20px;width:400px;height:225px;box-shadow:0 2px 5px 0 rgba(0,0,0,.16),0 3px 6px 0 rgba(0,0,0,.2)}.artplayer-video-player.artplayer-pip .artplayer-pip-header{display:flex}.artplayer-video-player.artplayer-pip.artplayer-hover .artplayer-pip-header{opacity:1;visibility:visible}.artplayer-video-player.artplayer-pip .artplayer-mask{display:flex;align-items:center;justify-content:center}.artplayer-video-player.artplayer-pip .artplayer-mask .art-playBig{position:static}.artplayer-video-player.artplayer-pip .artplayer-bottom,.artplayer-video-player.artplayer-pip .artplayer-contextmenu,.artplayer-video-player.artplayer-pip .artplayer-danmu,.artplayer-video-player.artplayer-pip .artplayer-info,.artplayer-video-player.artplayer-pip .artplayer-layers,.artplayer-video-player.artplayer-pip .artplayer-notice,.artplayer-video-player.artplayer-pip .artplayer-setting,.artplayer-video-player.artplayer-pip .artplayer-subtitle{display:none!important}
-.artplayer-video-player.artplayer-web-fullscreen{position:fixed;z-index:9999;width:100%;height:100%;left:0;top:0;right:0;bottom:0}
.artplayer-video-player[data-flip=horizontal] .artplayer-video{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.artplayer-video-player[data-flip=vertical] .artplayer-video{-webkit-transform:scaleY(-1);transform:scaleY(-1)}
.artplayer-auto-size{display:flex;align-items:center;justify-content:center}
button[data-balloon]{overflow:visible}[data-balloon]{position:relative;cursor:pointer}[data-balloon]:after{font-family:sans-serif!important;font-weight:400!important;font-style:normal!important;text-shadow:none!important;font-size:12px!important;background:rgba(17,17,17,.9);border-radius:4px;color:#fff;content:attr(data-balloon);padding:.5em 1em;white-space:nowrap}[data-balloon]:after,[data-balloon]:before{filter:alpha(opactiy=0);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";-moz-opacity:0;-khtml-opacity:0;opacity:0;pointer-events:none;transition:all .18s ease-out .18s;position:absolute;z-index:10}[data-balloon]:before{background:no-repeat url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='12'%3E%3Cpath fill='rgba(17,17,17,0.9)' d='M2.658 0h32.004c-6 0-11.627 12.002-16.002 12.002C14.285 12.002 8.594 0 2.658 0z'/%3E%3C/svg%3E");background-size:100% auto;width:18px;height:6px;content:""}[data-balloon]:hover:after,[data-balloon]:hover:before,[data-balloon][data-balloon-visible]:after,[data-balloon][data-balloon-visible]:before{filter:alpha(opactiy=100);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";-moz-opacity:1;-khtml-opacity:1;opacity:1;pointer-events:auto}[data-balloon].font-awesome:after{font-family:FontAwesome}[data-balloon][data-balloon-break]:after{white-space:pre}[data-balloon][data-balloon-blunt]:after,[data-balloon][data-balloon-blunt]:before{transition:none}[data-balloon][data-balloon-pos=up]:after{margin-bottom:11px}[data-balloon][data-balloon-pos=up]:after,[data-balloon][data-balloon-pos=up]:before{bottom:100%;left:50%;-webkit-transform:translate(-50%,10px);transform:translate(-50%,10px);-webkit-transform-origin:top;transform-origin:top}[data-balloon][data-balloon-pos=up]:before{margin-bottom:5px}[data-balloon][data-balloon-pos=up]:hover:after,[data-balloon][data-balloon-pos=up]:hover:before,[data-balloon][data-balloon-pos=up][data-balloon-visible]:after,[data-balloon][data-balloon-pos=up][data-balloon-visible]:before{-webkit-transform:translate(-50%);transform:translate(-50%)}[data-balloon][data-balloon-pos=up-left]:after{left:0;margin-bottom:11px}[data-balloon][data-balloon-pos=up-left]:after,[data-balloon][data-balloon-pos=up-left]:before{bottom:100%;-webkit-transform:translateY(10px);transform:translateY(10px);-webkit-transform-origin:top;transform-origin:top}[data-balloon][data-balloon-pos=up-left]:before{left:5px;margin-bottom:5px}[data-balloon][data-balloon-pos=up-left]:hover:after,[data-balloon][data-balloon-pos=up-left]:hover:before,[data-balloon][data-balloon-pos=up-left][data-balloon-visible]:after,[data-balloon][data-balloon-pos=up-left][data-balloon-visible]:before{-webkit-transform:translate(0);transform:translate(0)}[data-balloon][data-balloon-pos=up-right]:after{right:0;margin-bottom:11px}[data-balloon][data-balloon-pos=up-right]:after,[data-balloon][data-balloon-pos=up-right]:before{bottom:100%;-webkit-transform:translateY(10px);transform:translateY(10px);-webkit-transform-origin:top;transform-origin:top}[data-balloon][data-balloon-pos=up-right]:before{right:5px;margin-bottom:5px}[data-balloon][data-balloon-pos=up-right]:hover:after,[data-balloon][data-balloon-pos=up-right]:hover:before,[data-balloon][data-balloon-pos=up-right][data-balloon-visible]:after,[data-balloon][data-balloon-pos=up-right][data-balloon-visible]:before{-webkit-transform:translate(0);transform:translate(0)}[data-balloon][data-balloon-pos=down]:after{margin-top:11px}[data-balloon][data-balloon-pos=down]:after,[data-balloon][data-balloon-pos=down]:before{left:50%;top:100%;-webkit-transform:translate(-50%,-10px);transform:translate(-50%,-10px)}[data-balloon][data-balloon-pos=down]:before{background:no-repeat url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='12'%3E%3Cpath fill='rgba(17,17,17,0.9)' d='M33.342 12H1.338c6 0 11.627-12.002 16.002-12.002C21.715-.002 27.406 12 33.342 12z'/%3E%3C/svg%3E");background-size:100% auto;width:18px;height:6px;margin-top:5px}[data-balloon][data-balloon-pos=down]:hover:after,[data-balloon][data-balloon-pos=down]:hover:before,[data-balloon][data-balloon-pos=down][data-balloon-visible]:after,[data-balloon][data-balloon-pos=down][data-balloon-visible]:before{-webkit-transform:translate(-50%);transform:translate(-50%)}[data-balloon][data-balloon-pos=down-left]:after{left:0;margin-top:11px;top:100%;-webkit-transform:translateY(-10px);transform:translateY(-10px)}[data-balloon][data-balloon-pos=down-left]:before{background:no-repeat url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='12'%3E%3Cpath fill='rgba(17,17,17,0.9)' d='M33.342 12H1.338c6 0 11.627-12.002 16.002-12.002C21.715-.002 27.406 12 33.342 12z'/%3E%3C/svg%3E");background-size:100% auto;width:18px;height:6px;left:5px;margin-top:5px;top:100%;-webkit-transform:translateY(-10px);transform:translateY(-10px)}[data-balloon][data-balloon-pos=down-left]:hover:after,[data-balloon][data-balloon-pos=down-left]:hover:before,[data-balloon][data-balloon-pos=down-left][data-balloon-visible]:after,[data-balloon][data-balloon-pos=down-left][data-balloon-visible]:before{-webkit-transform:translate(0);transform:translate(0)}[data-balloon][data-balloon-pos=down-right]:after{right:0;margin-top:11px;top:100%;-webkit-transform:translateY(-10px);transform:translateY(-10px)}[data-balloon][data-balloon-pos=down-right]:before{background:no-repeat url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='12'%3E%3Cpath fill='rgba(17,17,17,0.9)' d='M33.342 12H1.338c6 0 11.627-12.002 16.002-12.002C21.715-.002 27.406 12 33.342 12z'/%3E%3C/svg%3E");background-size:100% auto;width:18px;height:6px;right:5px;margin-top:5px;top:100%;-webkit-transform:translateY(-10px);transform:translateY(-10px)}[data-balloon][data-balloon-pos=down-right]:hover:after,[data-balloon][data-balloon-pos=down-right]:hover:before,[data-balloon][data-balloon-pos=down-right][data-balloon-visible]:after,[data-balloon][data-balloon-pos=down-right][data-balloon-visible]:before{-webkit-transform:translate(0);transform:translate(0)}[data-balloon][data-balloon-pos=left]:after{margin-right:11px;right:100%;top:50%;-webkit-transform:translate(10px,-50%);transform:translate(10px,-50%)}[data-balloon][data-balloon-pos=left]:before{background:no-repeat url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='36'%3E%3Cpath fill='rgba(17,17,17,0.9)' d='M0 33.342V1.338c0 6 12.002 11.627 12.002 16.002C12.002 21.715 0 27.406 0 33.342z'/%3E%3C/svg%3E");background-size:100% auto;width:6px;height:18px;margin-right:5px;right:100%;top:50%;-webkit-transform:translate(10px,-50%);transform:translate(10px,-50%)}[data-balloon][data-balloon-pos=left]:hover:after,[data-balloon][data-balloon-pos=left]:hover:before,[data-balloon][data-balloon-pos=left][data-balloon-visible]:after,[data-balloon][data-balloon-pos=left][data-balloon-visible]:before{-webkit-transform:translateY(-50%);transform:translateY(-50%)}[data-balloon][data-balloon-pos=right]:after{left:100%;margin-left:11px;top:50%;-webkit-transform:translate(-10px,-50%);transform:translate(-10px,-50%)}[data-balloon][data-balloon-pos=right]:before{background:no-repeat url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='36'%3E%3Cpath fill='rgba(17,17,17,0.9)' d='M12 2.658v32.004c0-6-12.002-11.627-12.002-16.002C-.002 14.285 12 8.594 12 2.658z'/%3E%3C/svg%3E");background-size:100% auto;width:6px;height:18px;left:100%;margin-left:5px;top:50%;-webkit-transform:translate(-10px,-50%);transform:translate(-10px,-50%)}[data-balloon][data-balloon-pos=right]:hover:after,[data-balloon][data-balloon-pos=right]:hover:before,[data-balloon][data-balloon-pos=right][data-balloon-visible]:after,[data-balloon][data-balloon-pos=right][data-balloon-visible]:before{-webkit-transform:translateY(-50%);transform:translateY(-50%)}[data-balloon][data-balloon-length=small]:after{white-space:normal;width:80px}[data-balloon][data-balloon-length=medium]:after{white-space:normal;width:150px}[data-balloon][data-balloon-length=large]:after{white-space:normal;width:260px}[data-balloon][data-balloon-length=xlarge]:after{white-space:normal;width:380px}@media screen and (max-width:768px){[data-balloon][data-balloon-length=xlarge]:after{white-space:normal;width:90vw}}[data-balloon][data-balloon-length=fit]:after{white-space:normal;width:100%}
\ No newline at end of file
diff --git a/docs/uncompiled/artplayer.js b/docs/uncompiled/artplayer.js
index 6791bce56..05f20f192 100644
--- a/docs/uncompiled/artplayer.js
+++ b/docs/uncompiled/artplayer.js
@@ -622,6 +622,15 @@
type: 'text/vtt'
}));
}
+ function downloadImage(url, name) {
+ var elink = document.createElement('a');
+ setStyle(elink, 'display', 'none');
+ elink.href = url;
+ elink.download = name;
+ document.body.appendChild(elink);
+ elink.click();
+ document.body.removeChild(elink);
+ }
var utils = /*#__PURE__*/Object.freeze({
ArtPlayerError: ArtPlayerError,
@@ -644,7 +653,8 @@
inverseClass: inverseClass,
tooltip: tooltip,
srtToVtt: srtToVtt,
- vttToBlob: vttToBlob
+ vttToBlob: vttToBlob,
+ downloadImage: downloadImage
});
function validElement(paths, value, type) {
@@ -1501,13 +1511,7 @@
canvas.height = $video.videoHeight;
canvas.getContext('2d').drawImage($video, 0, 0);
var dataUri = canvas.toDataURL('image/png');
- var elink = document.createElement('a');
- setStyle(elink, 'display', 'none');
- elink.href = dataUri;
- elink.download = "".concat(option.title || 'artplayer', "_").concat(secondToTime($video.currentTime), ".png");
- document.body.appendChild(elink);
- elink.click();
- document.body.removeChild(elink);
+ downloadImage(dataUri, "".concat(option.title || 'artplayer', "_").concat(secondToTime($video.currentTime), ".png"));
return dataUri;
}
diff --git a/packages/artplayer-plugin-gif/.npmignore b/packages/artplayer-plugin-gif/.npmignore
new file mode 100644
index 000000000..429145126
--- /dev/null
+++ b/packages/artplayer-plugin-gif/.npmignore
@@ -0,0 +1,9 @@
+node_modules
+.DS_Store
+yarn.lock
+package-lock.json
+test
+coverage
+.nyc_output
+.cache
+docs
\ No newline at end of file
diff --git a/packages/artplayer-plugin-gif/.npmrc b/packages/artplayer-plugin-gif/.npmrc
new file mode 100644
index 000000000..9cf949503
--- /dev/null
+++ b/packages/artplayer-plugin-gif/.npmrc
@@ -0,0 +1 @@
+package-lock=false
\ No newline at end of file
diff --git a/packages/artplayer-plugin-gif/README.md b/packages/artplayer-plugin-gif/README.md
new file mode 100644
index 000000000..c4e27d982
--- /dev/null
+++ b/packages/artplayer-plugin-gif/README.md
@@ -0,0 +1,63 @@
+# artplayer-plugin-gif
+
+Gif plugin for ArtPlayer, use [gifshot](https://github.com/yahoo/gifshot) as a dependency
+
+## Demo
+
+[Checkout the demo](https://artplayer.org/gif/) from Github Pages
+
+## Install
+
+Install with `npm`
+
+```
+$ npm install artplayer-plugin-gif
+```
+
+Or install with `yarn`
+
+```
+$ yarn add artplayer-plugin-gif
+```
+
+```js
+import artplayerPluginGif from 'artplayer-plugin-gif';
+```
+
+Or umd builds are also available
+
+```html
+
+```
+
+Will expose the global variable to `window.artplayerPluginGif`.
+
+## Usage
+
+This will add a button called "GIF" in the bottom control bar.
+
+```js
+var art = new Artplayer({
+ container: '.artplayer-app',
+ url: 'path/to/video.mp4',
+ plugins: [artplayerPluginGif],
+});
+```
+
+Or can be called via api
+
+```js
+art.plugins.artplayerPluginGif.create(
+ {
+ // some configuration
+ // https://github.com/yahoo/gifshot#options
+ },
+ function(imageUrl) {
+ console.log(imageUrl);
+ },
+);
+```
+
+## License
+
+MIT © [Harvey Zack](https://www.zhw-island.com/)
diff --git a/packages/artplayer-plugin-gif/package.json b/packages/artplayer-plugin-gif/package.json
new file mode 100644
index 000000000..ca1d957a3
--- /dev/null
+++ b/packages/artplayer-plugin-gif/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "artplayer-plugin-gif",
+ "version": "1.0.5",
+ "description": "Gif plugin for ArtPlayer",
+ "main": "dist/artplayer-plugin-gif.js",
+ "style": "dist/artplayer-plugin-gif.css",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/zhw2590582/ArtPlayer.git"
+ },
+ "keywords": [
+ "video",
+ "player"
+ ],
+ "author": "Harvey Zack ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/zhw2590582/ArtPlayer/issues"
+ },
+ "homepage": "https://github.com/zhw2590582/ArtPlayer#readme",
+ "dependencies": {
+ "gifshot": "^0.4.5"
+ }
+}
diff --git a/packages/artplayer-plugin-gif/src/index.js b/packages/artplayer-plugin-gif/src/index.js
new file mode 100644
index 000000000..cb81ea917
--- /dev/null
+++ b/packages/artplayer-plugin-gif/src/index.js
@@ -0,0 +1,145 @@
+import gifshot from 'gifshot';
+
+function i18nMix(i18n) {
+ i18n.update({
+ 'zh-cn': {
+ 'Long press, gif length is between 1 second and 5 seconds': '长按,gif 长度为 1 ~ 5 秒',
+ 'Gif time is too short': 'Gif 时间太短',
+ 'Start creating gif, please wait': '开始创建 gif,请稍等',
+ 'Create gif successfully': '创建 gif 成功',
+ 'There is another gif in the processing': '正有另一个 gif 在创建中',
+ },
+ 'zh-tw': {
+ 'Long press, gif length is between 1 second and 5 seconds': '長按,gif 長度為 1 ~ 5 秒',
+ 'Gif time is too short': 'Gif 時間太短',
+ 'Start creating gif, please wait': '開始創建 gif,請稍等',
+ 'Create gif successfully': '創建 gif 成功',
+ 'There is another gif in the processing': '正有另一個 gif 在創建中',
+ },
+ });
+}
+
+function artplayerPluginGif(art) {
+ const { errorHandle, clamp, downloadImage } = art.constructor.utils;
+ const {
+ i18n,
+ notice,
+ layers,
+ controls,
+ player,
+ option: { theme, title },
+ events: { proxy },
+ template: { $video },
+ } = art;
+
+ i18nMix(i18n);
+
+ layers.add({
+ name: 'artplayer-plugin-gif-progress',
+ html: '',
+ style: {
+ position: 'absolute',
+ top: '0',
+ left: '0',
+ height: '3px',
+ width: '0%',
+ 'background-color': theme,
+ },
+ });
+
+ const $progress = layers['artplayer-plugin-gif-progress'].$ref;
+ let isProcessing = false;
+ let pressStartTime = 0;
+ let progressTimer = null;
+
+ function cleanTimer() {
+ $progress.style.width = '0%';
+ clearTimeout(progressTimer);
+ progressTimer = null;
+ }
+
+ function createGif() {
+ cleanTimer();
+ const pressTime = new Date() - pressStartTime;
+
+ if (isProcessing) {
+ notice.show(i18n.get('There is another gif in the processing'));
+ } else if (pressTime < 1000) {
+ notice.show(i18n.get('Gif time is too short'));
+ } else {
+ const numFrames = Math.floor(clamp(pressTime, 1000, 5000) / 100);
+ const { videoWidth, videoHeight } = $video;
+ art.plugins.artplayerPluginGif.create(
+ {
+ numFrames,
+ gifHeight: 200,
+ gifWidth: (videoWidth / videoHeight) * 200,
+ },
+ image => {
+ downloadImage(image, `${title || 'unnamed'}.gif`);
+ },
+ );
+ }
+ }
+
+ controls.add({
+ name: 'artplayer-plugin-gif',
+ position: 'right',
+ html: 'GIF',
+ mounted: $gif => {
+ proxy($gif, 'mousedown', () => {
+ cleanTimer();
+ pressStartTime = new Date();
+ notice.show(i18n.get('Long press, gif length is between 1 second and 5 seconds'));
+
+ (function loop() {
+ progressTimer = setTimeout(() => {
+ const width = parseInt($progress.style.width, 10);
+ if (width <= 100) {
+ $progress.style.width = `${width + 1}%`;
+ loop();
+ } else {
+ createGif();
+ }
+ }, 50);
+ })();
+ });
+
+ proxy($gif, 'mouseup', () => {
+ createGif();
+ });
+ },
+ });
+
+ return {
+ name: 'artplayerPluginGif',
+ isProcessing,
+ create(config = {}, callback) {
+ isProcessing = true;
+ art.emit('artplayerPluginGif:create:start');
+ notice.show(`${i18n.get('Start creating gif, please wait')}: ${config.numFrames / 10 || 1} s`, false, 5000);
+ gifshot.createGIF(
+ {
+ ...config,
+ offset: player.currentTime,
+ video: [$video.src],
+ },
+ obj => {
+ if (obj.error) {
+ notice.show(obj.errorMsg);
+ errorHandle(false, obj.errorMsg);
+ } else if (typeof callback === 'function') {
+ callback(obj.image);
+ notice.show(i18n.get('Create gif successfully'));
+ }
+
+ isProcessing = false;
+ art.emit('artplayerPluginGif:create:end');
+ },
+ );
+ },
+ };
+}
+
+window.artplayerPluginGif = artplayerPluginGif;
+export default artplayerPluginGif;
diff --git a/packages/artplayer/src/player/screenshotMix.js b/packages/artplayer/src/player/screenshotMix.js
index 0a954ea8d..830d58cc9 100644
--- a/packages/artplayer/src/player/screenshotMix.js
+++ b/packages/artplayer/src/player/screenshotMix.js
@@ -1,4 +1,4 @@
-import { setStyle, secondToTime } from '../utils';
+import { secondToTime, downloadImage } from '../utils';
export default function screenshotMix(art, player) {
const {
@@ -13,13 +13,7 @@ export default function screenshotMix(art, player) {
canvas.height = $video.videoHeight;
canvas.getContext('2d').drawImage($video, 0, 0);
const dataUri = canvas.toDataURL('image/png');
- const elink = document.createElement('a');
- setStyle(elink, 'display', 'none');
- elink.href = dataUri;
- elink.download = `${option.title || 'artplayer'}_${secondToTime($video.currentTime)}.png`;
- document.body.appendChild(elink);
- elink.click();
- document.body.removeChild(elink);
+ downloadImage(dataUri, `${option.title || 'artplayer'}_${secondToTime($video.currentTime)}.png`);
return dataUri;
}
diff --git a/packages/artplayer/src/style/bottom.scss b/packages/artplayer/src/style/bottom.scss
index aa8d707be..d27cfc73c 100644
--- a/packages/artplayer/src/style/bottom.scss
+++ b/packages/artplayer/src/style/bottom.scss
@@ -136,7 +136,9 @@
opacity: .9;
font-size: 12px;
height: 36px;
+ min-width: 36px;
line-height: 36px;
+ text-align: center;
cursor: pointer;
.art-icon {
display: flex;
diff --git a/packages/artplayer/src/utils/index.js b/packages/artplayer/src/utils/index.js
index 2de572a34..91ac2512d 100644
--- a/packages/artplayer/src/utils/index.js
+++ b/packages/artplayer/src/utils/index.js
@@ -170,3 +170,13 @@ export function vttToBlob(vttText) {
}),
);
}
+
+export function downloadImage(url, name) {
+ const elink = document.createElement('a');
+ setStyle(elink, 'display', 'none');
+ elink.href = url;
+ elink.download = name;
+ document.body.appendChild(elink);
+ elink.click();
+ document.body.removeChild(elink);
+}
\ No newline at end of file