From ca313572927d393a11c38655eb4c0f399fef371d Mon Sep 17 00:00:00 2001 From: Harvey Zack Date: Thu, 6 Dec 2018 11:01:24 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96gif=E6=8F=92?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- docs/gif/css/common.css | 10 + docs/gif/css/consola.css | 1 + docs/gif/css/normalize.css | 461 ++++ docs/gif/img/favicon.ico | Bin 0 -> 9662 bytes docs/gif/index.html | 37 + docs/gif/js/common.js | 34 + docs/gif/js/consola.js | 1 + docs/thumbnail/css/common.css | 2 +- docs/thumbnail/js/common.js | 2 +- docs/uncompiled/artplayer-plugin-gif.js | 2697 ++++++++++++++++++++ packages/artplayer-plugin-gif/.npmignore | 9 + packages/artplayer-plugin-gif/.npmrc | 1 + packages/artplayer-plugin-gif/README.md | 3 + packages/artplayer-plugin-gif/package.json | 27 + packages/artplayer-plugin-gif/src/index.js | 11 + 16 files changed, 3296 insertions(+), 3 deletions(-) create mode 100644 docs/gif/css/common.css create mode 100644 docs/gif/css/consola.css create mode 100644 docs/gif/css/normalize.css create mode 100644 docs/gif/img/favicon.ico create mode 100644 docs/gif/index.html create mode 100644 docs/gif/js/common.js create mode 100644 docs/gif/js/consola.js create mode 100644 docs/uncompiled/artplayer-plugin-gif.js create mode 100644 packages/artplayer-plugin-gif/.npmignore create mode 100644 packages/artplayer-plugin-gif/.npmrc create mode 100644 packages/artplayer-plugin-gif/README.md create mode 100644 packages/artplayer-plugin-gif/package.json create mode 100644 packages/artplayer-plugin-gif/src/index.js 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/gif/css/common.css b/docs/gif/css/common.css new file mode 100644 index 000000000..d6936ec1e --- /dev/null +++ b/docs/gif/css/common.css @@ -0,0 +1,10 @@ +html, body, .container { + height: 100%; +} + +.artplayer { + width: 800px; + height: 500px; + margin: 20px auto 0; + background: #fff; +} \ No newline at end of file diff --git a/docs/gif/css/consola.css b/docs/gif/css/consola.css new file mode 100644 index 000000000..b6bcaa2a0 --- /dev/null +++ b/docs/gif/css/consola.css @@ -0,0 +1 @@ +.consola-container{opacity:.9;display:flex;flex-direction:column;position:absolute;background-color:#242424}.consola-container.right{top:0;right:0;bottom:0;height:100%;width:0}.consola-container.bottom{left:0;right:0;bottom:0;width:100%;height:0}.consola-container .consola-tools{left:0;right:0;top:0;z-index:2;display:flex;justify-content:space-between;align-items:center;flex-direction:row;height:30px;padding:0 10px;color:hsla(0,0%,100%,.8);background-color:#141618;border-bottom:1px solid rgba(0,0,0,.3);font-size:12px;box-sizing:border-box}.consola-container .consola-tools .consola-clean{cursor:pointer}.consola-container .consola-wrap{flex:1;position:relative;z-index:1;overflow-y:auto;word-break:break-word;box-sizing:border-box}.consola-container .consola-wrap .method.method-log{position:relative;display:flex;color:hsla(0,0%,100%,.9);background-color:transparent;border-top:1px solid hsla(0,0%,100%,.05);border-bottom:1px solid hsla(0,0%,100%,.05);margin-top:-1px;margin-bottom:0;padding-left:10px;box-sizing:border-box}.consola-container .consola-wrap .method.method-log .method-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABpSURBVChTY6A92LNnj96uXbvKoVzsYMeOHVbbt29/D1T4eP/+/QJQYVSwe/duD6CCr0B8A8iWgwqjAqBk2NatW38B6bPbtm0TBYkBFbsA+c9ANFgRCBCtEASAAoSthgGiPAMD2IOHgQEA521bM7uG52wAAAAASUVORK5CYII=)}.consola-container .consola-wrap .method.method-info{position:relative;display:flex;color:hsla(0,0%,100%,.9);background-color:transparent;border-top:1px solid hsla(0,0%,100%,.05);border-bottom:1px solid hsla(0,0%,100%,.05);margin-top:-1px;margin-bottom:0;padding-left:10px;box-sizing:border-box}.consola-container .consola-wrap .method.method-info .method-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADISURBVChTY4ABp/AztmZBZ07qe538rO114rOa8+GTskYHbKHSEOARd6nLIOTsf61gIA46U6kVePYQiK3uc/K/hPG+LrCi8IyrtkZh5yCKgk/80w46ba0RdGYGhH/2v6rXyf88qtttGVwSLp2ECQLxeiAu1wo6uwpJ7L+o2f6TDA6xZz8jCyqFnuHXCj4djywmZXHoM/EK0azGqhBsNYpngL6VCTnGqRF4xgKo+D5IDO4ZEEAKnjcQBafvqwWf/YoSPDCAP8AZGAC7mLM81zgOTQAAAABJRU5ErkJggg==)}.consola-container .consola-wrap .method.method-warn{position:relative;display:flex;color:#ffdc9e;background-color:#332b00;border-top:1px solid #650;border-bottom:1px solid #650;margin-top:-1px;margin-bottom:1px;padding-left:10px;box-sizing:border-box}.consola-container .consola-wrap .method.method-warn .method-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACkSURBVChTbY7BCoJQFERn0Q/3BX1JuxQjsSCXiV8gtCgxhCIrKIRIqKDVzXl5w5cNHBjm6eGinXiAXu5inY2xYm/mbpIh+vcFhLA3sx0athNUhymEsP+10lAEEA17x8o/9wFuNGnYuVlWve0SQl7P0sBu3aq2R1Q/1JzSkYGd29eqNv2wjdnUuvNRciC/N+qe+7gidbA8zyHkOINsvA/sumcOkjcabcBmw2+mMgAAAABJRU5ErkJggg==)}.consola-container .consola-wrap .method.method-error{position:relative;display:flex;color:#ff8080;background-color:#290000;border-top:1px solid #5b0000;border-bottom:1px solid #5b0000;margin-top:-1px;margin-bottom:1px;padding-left:10px;box-sizing:border-box}.consola-container .consola-wrap .method.method-error .method-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADESURBVChTY4CB7ZI8tmfU5E6e01b+DMIgNkgMKg0BR9Vkux6YWPx/bemIgkFiIDmwogOaqrYPzazAEm8DwuGKYGyQHEgNw0VT05Mwib9v3v7/kJEHxiA2TDFIDcNNU4vPMFPACj58/P/v40cwGyYOUsNwy8IZRSFIEUgxskKQGoZrzp4ErQapYbgYHG371M4dLACTQGaD5EBqwD6/FpzQ9dTBE64IhkFiIDmwIhi4mlJqey8o4eR9r8jPIAxig8QgsgwMAFZz1YtGPXgjAAAAAElFTkSuQmCC)}.consola-container .consola-wrap .method.method-debug{position:relative;display:flex;color:#4d88ff;background-color:transparent;border-top:1px solid hsla(0,0%,100%,.05);border-bottom:1px solid hsla(0,0%,100%,.05);margin-top:-1px;margin-bottom:0;padding-left:10px;box-sizing:border-box}.consola-container .consola-wrap .method.method-debug .method-icon{background-image:url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 459 459'%3e%3cpath fill='%234D88FF' d='M433.5 127.5h-71.4a177.7 177.7 0 0 0-45.9-51L357 35.7 321.3 0l-56.1 56.1c-10.2-2.6-23-5.1-35.7-5.1s-25.5 2.5-35.7 5.1L137.7 0 102 35.7l40.8 40.8a177.7 177.7 0 0 0-45.9 51H25.5v51H79c-2.5 7.7-2.5 17.9-2.5 25.5v25.5h-51v51h51V306a88 88 0 0 0 2.5 25.5H25.5v51h71.4A152.2 152.2 0 0 0 229.5 459c56.1 0 107.1-30.6 132.6-76.5h71.4v-51H380c2.5-7.7 2.5-17.9 2.5-25.5v-25.5h51v-51h-51V204c0-7.7 0-17.9-2.5-25.5h53.5v-51zm-153 204h-102v-51h102v51zm0-102h-102v-51h102v51z'/%3e%3c/svg%3e")}.consola-container .consola-wrap .method *{vertical-align:top;box-sizing:border-box;font-family:Consolas,Lucida Console,Courier New,monospace;white-space:pre-wrap;font-size:12px}.consola-container .consola-wrap .method .method-icon{width:10px;height:18px;background-image:none;background-repeat:no-repeat;background-position:50% 50%}.consola-container .consola-wrap .method .method-text{clear:right;position:relative;padding:3px 22px 2px 0;margin-left:15px;min-height:18px;flex:auto;width:calc(100% - 15px)} diff --git a/docs/gif/css/normalize.css b/docs/gif/css/normalize.css new file mode 100644 index 000000000..9b77e0eb4 --- /dev/null +++ b/docs/gif/css/normalize.css @@ -0,0 +1,461 @@ +/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ + +/** + * 1. Change the default font family in all browsers (opinionated). + * 2. Correct the line height in all browsers. + * 3. Prevent adjustments of font size after orientation changes in + * IE on Windows Phone and in iOS. + */ + +/* Document + ========================================================================== */ + +html { + font-family: sans-serif; /* 1 */ + line-height: 1.15; /* 2 */ + -ms-text-size-adjust: 100%; /* 3 */ + -webkit-text-size-adjust: 100%; /* 3 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers (opinionated). + */ + +body { + margin: 0; +} + +/** + * Add the correct display in IE 9-. + */ + +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + * 1. Add the correct display in IE. + */ + +figcaption, +figure, +main { /* 1 */ + display: block; +} + +/** + * Add the correct margin in IE 8. + */ + +figure { + margin: 1em 40px; +} + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * 1. Remove the gray background on active links in IE 10. + * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. + */ + +a { + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ +} + +/** + * Remove the outline on focused links when they are also active or hovered + * in all browsers (opinionated). + */ + +a:active, +a:hover { + outline-width: 0; +} + +/** + * 1. Remove the bottom border in Firefox 39-. + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Prevent the duplicate application of `bolder` by the next rule in Safari 6. + */ + +b, +strong { + font-weight: inherit; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font style in Android 4.3-. + */ + +dfn { + font-style: italic; +} + +/** + * Add the correct background and color in IE 9-. + */ + +mark { + background-color: #ff0; + color: #000; +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images inside links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers (opinionated). + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: sans-serif; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` + * controls in Android 4. + * 2. Correct the inability to style clickable types in iOS and Safari. + */ + +button, +html [type="button"], /* 1 */ +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 2 */ +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Change the border, margin, and padding in all browsers (opinionated). + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in IE 9-. + * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the default vertical scrollbar in IE. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in IE 9-. + * 1. Add the correct display in Edge, IE, and Firefox. + */ + +details, /* 1 */ +menu { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Scripting + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* Hidden + ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +} diff --git a/docs/gif/img/favicon.ico b/docs/gif/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1b9f010e8b4a2cb5dc7d947b4d4b09b7b859b7ce GIT binary patch literal 9662 zcmeI2TdZ7D7{}M-sMEM~1gS$vT`JK&G)*cVq|vwp6(I-_iEC0N9$e#rlb$&a8X~Qr zX*T<0Z~Bjdac}{!w3&|2Z}~Rl^Q>V?TVrT_ zI;~%!KM!J^C}E4Phub`JyrRW5Z!6#qVC{C&KLw=Gz6hs)weP0>ks#LdCTXbCEiQ{z zp|CZneN~F_4Jmfr)W0E!r}^UPCK|q`-)JA;6&d_pWAFWRjd+?(V+Zg$&pWOETw*nX zcn{O}q_{puO#KI9|BLbQzMEPBH8;J?@-tidcG!Lxhx1S|?f8$=_jzLK$K0J}e7vvt zTVQL3?iJ)$f`dRTZNbpS_ibH^C7|=*W#X~*W9l9Xd~KY(_OJM@0egWwx*ug$obBkU zDf)Km$K8FL5+f^TDE}JY6$+9<%aMilvHiIz@v=mRp!mIuT&n3ga4_(snA$VM)@$v) z!EcTa6v@BsFqh3+BZeMHM4tewi)rgRIUei-+99o#--DPOMK&7qi4J0YgiojRPu5>_ zd|CRlJ1ri+|3du>ck}xFb<@dV%);qAkacc*Az#o*;l^j@4A*~zsioa!);^*a3vQ(HesB0`tK3X?qzNIC+YZDC(u5}^u65j zP1ZT5Js;i61+i>gwRO+@5NO}2vA25dCl3W_^cq5Io7cWOEkKS;>)Y!E@t;7idyZXm z_Mm?r*amz(U((K^cTjk{JbqSIfCBj<^w)t$z*As7SP6~Tn=U3PLOwbP`RXv(tc{B^9#zbpaSpV`o$A4M3xmC|kk$Em}+52M{j`Yhi(=*`3 z;AU`XmQjxURdlLF@2Fzv7L0uDt705-Z1gM@t|y-%J3YrIRB7I*9?jtx()-;Pz>*l_ z92>1aejL)c&T|Y+VkCdP*XSkr`$>bO7lDP~RAAS|+vw-fMwI^8wT|_gP9ougAgQj0 zt-#l{miAC!a~%B_jm^41Khs^^uk10@>8svn!FJ$}Z6@Xo>7z;GYHJPK_e4jUIm2T~ zeLL6!szC3&wIBc6dx-x32m1e?=+A-OdJgFH>Hi<;yk4%CrJpL-2jQhgIT>5cN&uf= zMW_r<^V-sBULPtaqsZ&C%Sj17REwX$<>Dg*heE~#kOUvo*dG^&?NNxfWc@+2Wxqr@=4ld+^e pBECAQ%@bdnRLkN?QXQ-{`Q+$ClPBXkE~uvCfa>HE&mGs6{!c$q56A!j literal 0 HcmV?d00001 diff --git a/docs/gif/index.html b/docs/gif/index.html new file mode 100644 index 000000000..40157bab1 --- /dev/null +++ b/docs/gif/index.html @@ -0,0 +1,37 @@ + + + + + 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..53d4c62a4 --- /dev/null +++ b/docs/gif/js/common.js @@ -0,0 +1,34 @@ +'use strict'; + +(function() { + consola.creat({ + target: 'body', + 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', + autoSize: true, + controls: [ + { + name: 'preview', + position: 'right', + html: '打开' + }, + ], + plugins: [artplayerPluginGif] + }); + + art.on('firstCanplay', () => { + var preview = document.querySelector('.art-control-preview'); + art.plugins.localPreview.attach(preview); + }); +})(); diff --git a/docs/gif/js/consola.js b/docs/gif/js/consola.js new file mode 100644 index 000000000..7fa05c34c --- /dev/null +++ b/docs/gif/js/consola.js @@ -0,0 +1 @@ +!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",'
\n
\n
Console 0
\n
Clean
\n
\n
\n
'),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/thumbnail/css/common.css b/docs/thumbnail/css/common.css index 2f6576eeb..6bf018144 100644 --- a/docs/thumbnail/css/common.css +++ b/docs/thumbnail/css/common.css @@ -160,5 +160,5 @@ html, body { .rigth-part .console { position: relative; height: 30%; - } +} \ No newline at end of file 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/uncompiled/artplayer-plugin-gif.js b/docs/uncompiled/artplayer-plugin-gif.js new file mode 100644 index 000000000..974fc4738 --- /dev/null +++ b/docs/uncompiled/artplayer-plugin-gif.js @@ -0,0 +1,2697 @@ +(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 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 artplayerPluginGif(art) { + console.log(gifshot); + return { + name: 'artplayerPluginGif' + }; + } + + window.artplayerPluginGif = artplayerPluginGif; + + exports.default = artplayerPluginGif; + + Object.defineProperty(exports, '__esModule', { value: true }); + +}))); 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..543c3a97a --- /dev/null +++ b/packages/artplayer-plugin-gif/README.md @@ -0,0 +1,3 @@ +# artplayer-plugin-gif +Gif plugin for ArtPlayer + 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..74657325b --- /dev/null +++ b/packages/artplayer-plugin-gif/src/index.js @@ -0,0 +1,11 @@ +import gifshot from 'gifshot'; + +function artplayerPluginGif(art) { + console.log(gifshot); + return { + name: 'artplayerPluginGif', + }; +} + +window.artplayerPluginGif = artplayerPluginGif; +export default artplayerPluginGif; From b64d5d5744bf72c12df583d7f7d0d38a6c6c2f18 Mon Sep 17 00:00:00 2001 From: Harvey Zack Date: Thu, 6 Dec 2018 11:24:22 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=9E=84=E5=BB=BAdemo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/gif/css/common.css | 45 +- docs/gif/index.html | 10 +- docs/gif/js/common.js | 8 +- docs/uncompiled/artplayer-plugin-gif.js | 5436 ++++++++++---------- packages/artplayer-plugin-gif/src/index.js | 18 +- 5 files changed, 2819 insertions(+), 2698 deletions(-) diff --git a/docs/gif/css/common.css b/docs/gif/css/common.css index d6936ec1e..6851a54e2 100644 --- a/docs/gif/css/common.css +++ b/docs/gif/css/common.css @@ -2,9 +2,48 @@ html, body, .container { height: 100%; } -.artplayer { - width: 800px; +.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; - margin: 20px auto 0; background: #fff; +} + +.part-right .preview { + width: 500px; + height: 500px; + background: #f3f3f3; +} + +.part-right .preview img { + width: 100%; + max-width: 100%; } \ No newline at end of file diff --git a/docs/gif/index.html b/docs/gif/index.html index 40157bab1..ac6a7c013 100644 --- a/docs/gif/index.html +++ b/docs/gif/index.html @@ -17,7 +17,15 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/docs/gif/js/common.js b/docs/gif/js/common.js index 53d4c62a4..75e6bb828 100644 --- a/docs/gif/js/common.js +++ b/docs/gif/js/common.js @@ -2,7 +2,8 @@ (function() { consola.creat({ - target: 'body', + target: '.part-bottom', + size: '100%', zIndex: 99, }); @@ -30,5 +31,10 @@ art.on('firstCanplay', () => { var preview = document.querySelector('.art-control-preview'); art.plugins.localPreview.attach(preview); + art.plugins.artplayerPluginGif.create({}, image => { + var animatedImage = document.createElement('img'); + animatedImage.src = image; + document.querySelector('.preview').appendChild(animatedImage); + }); }); })(); diff --git a/docs/uncompiled/artplayer-plugin-gif.js b/docs/uncompiled/artplayer-plugin-gif.js index 974fc4738..51a6e480b 100644 --- a/docs/uncompiled/artplayer-plugin-gif.js +++ b/docs/uncompiled/artplayer-plugin-gif.js @@ -1,2697 +1,2749 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (factory((global['artplayer-plugin-gif'] = {}))); + 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 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 artplayerPluginGif(art) { - console.log(gifshot); - return { - name: 'artplayerPluginGif' - }; - } - - window.artplayerPluginGif = artplayerPluginGif; - - exports.default = artplayerPluginGif; - - Object.defineProperty(exports, '__esModule', { value: true }); + 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 artplayerPluginGif(art) { + return { + name: 'artplayerPluginGif', + create: function create() { + var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var callback = arguments.length > 1 ? arguments[1] : undefined; + gifshot.createGIF(objectSpread({}, config, { + video: [art.template.$video.src] + }), function (obj) { + if (!obj.error) { + if (callback) { + callback(obj.image); + } + } else { + console.log(obj.error); + } + }); + } + }; + } + + window.artplayerPluginGif = artplayerPluginGif; + + exports.default = artplayerPluginGif; + + Object.defineProperty(exports, '__esModule', { value: true }); }))); diff --git a/packages/artplayer-plugin-gif/src/index.js b/packages/artplayer-plugin-gif/src/index.js index 74657325b..766cc4557 100644 --- a/packages/artplayer-plugin-gif/src/index.js +++ b/packages/artplayer-plugin-gif/src/index.js @@ -1,9 +1,25 @@ import gifshot from 'gifshot'; function artplayerPluginGif(art) { - console.log(gifshot); return { name: 'artplayerPluginGif', + create(config = {}, callback) { + gifshot.createGIF( + { + ...config, + video: [art.template.$video.src], + }, + obj => { + if (!obj.error) { + if (callback) { + callback(obj.image); + } + } else { + console.log(obj.error); + } + }, + ); + }, }; } From d535423dd85cbaa127bae9fc97c58c3f333904a0 Mon Sep 17 00:00:00 2001 From: Harvey Zack Date: Thu, 6 Dec 2018 11:59:55 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=88=86=E7=A6=BB=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E8=B5=84=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/{lab => assets}/css/codemirror.css | 0 docs/{gif => assets}/css/consola.css | 0 docs/{gif => assets}/css/normalize.css | 0 docs/{gif => assets}/img/favicon.ico | Bin docs/{lab => assets}/js/codemirror.js | 0 docs/{gif => assets}/js/consola.js | 0 docs/{lab => assets}/js/javascript.js | 0 docs/gif/index.html | 8 +- docs/lab/css/consola.css | 1 - docs/lab/css/normalize.css | 461 ------------------------ docs/lab/img/favicon.ico | Bin 9662 -> 0 bytes docs/lab/index.html | 14 +- docs/lab/js/consola.js | 1 - docs/lab/mobile.html | 4 +- docs/thumbnail/css/consola.css | 1 - docs/thumbnail/css/normalize.css | 461 ------------------------ docs/thumbnail/img/favicon.ico | Bin 9662 -> 0 bytes docs/thumbnail/index.html | 10 +- docs/thumbnail/js/consola.js | 1 - 19 files changed, 18 insertions(+), 944 deletions(-) rename docs/{lab => assets}/css/codemirror.css (100%) rename docs/{gif => assets}/css/consola.css (100%) rename docs/{gif => assets}/css/normalize.css (100%) rename docs/{gif => assets}/img/favicon.ico (100%) rename docs/{lab => assets}/js/codemirror.js (100%) rename docs/{gif => assets}/js/consola.js (100%) rename docs/{lab => assets}/js/javascript.js (100%) delete mode 100644 docs/lab/css/consola.css delete mode 100644 docs/lab/css/normalize.css delete mode 100644 docs/lab/img/favicon.ico delete mode 100644 docs/lab/js/consola.js delete mode 100644 docs/thumbnail/css/consola.css delete mode 100644 docs/thumbnail/css/normalize.css delete mode 100644 docs/thumbnail/img/favicon.ico delete mode 100644 docs/thumbnail/js/consola.js 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/gif/css/consola.css b/docs/assets/css/consola.css similarity index 100% rename from docs/gif/css/consola.css rename to docs/assets/css/consola.css diff --git a/docs/gif/css/normalize.css b/docs/assets/css/normalize.css similarity index 100% rename from docs/gif/css/normalize.css rename to docs/assets/css/normalize.css diff --git a/docs/gif/img/favicon.ico b/docs/assets/img/favicon.ico similarity index 100% rename from docs/gif/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/gif/js/consola.js b/docs/assets/js/consola.js similarity index 100% rename from docs/gif/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/index.html b/docs/gif/index.html index ac6a7c013..25c172ab0 100644 --- a/docs/gif/index.html +++ b/docs/gif/index.html @@ -8,10 +8,10 @@ - - + + + - @@ -27,7 +27,7 @@
- + diff --git a/docs/lab/css/consola.css b/docs/lab/css/consola.css deleted file mode 100644 index b6bcaa2a0..000000000 --- a/docs/lab/css/consola.css +++ /dev/null @@ -1 +0,0 @@ -.consola-container{opacity:.9;display:flex;flex-direction:column;position:absolute;background-color:#242424}.consola-container.right{top:0;right:0;bottom:0;height:100%;width:0}.consola-container.bottom{left:0;right:0;bottom:0;width:100%;height:0}.consola-container .consola-tools{left:0;right:0;top:0;z-index:2;display:flex;justify-content:space-between;align-items:center;flex-direction:row;height:30px;padding:0 10px;color:hsla(0,0%,100%,.8);background-color:#141618;border-bottom:1px solid rgba(0,0,0,.3);font-size:12px;box-sizing:border-box}.consola-container .consola-tools .consola-clean{cursor:pointer}.consola-container .consola-wrap{flex:1;position:relative;z-index:1;overflow-y:auto;word-break:break-word;box-sizing:border-box}.consola-container .consola-wrap .method.method-log{position:relative;display:flex;color:hsla(0,0%,100%,.9);background-color:transparent;border-top:1px solid hsla(0,0%,100%,.05);border-bottom:1px solid hsla(0,0%,100%,.05);margin-top:-1px;margin-bottom:0;padding-left:10px;box-sizing:border-box}.consola-container .consola-wrap .method.method-log .method-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABpSURBVChTY6A92LNnj96uXbvKoVzsYMeOHVbbt29/D1T4eP/+/QJQYVSwe/duD6CCr0B8A8iWgwqjAqBk2NatW38B6bPbtm0TBYkBFbsA+c9ANFgRCBCtEASAAoSthgGiPAMD2IOHgQEA521bM7uG52wAAAAASUVORK5CYII=)}.consola-container .consola-wrap .method.method-info{position:relative;display:flex;color:hsla(0,0%,100%,.9);background-color:transparent;border-top:1px solid hsla(0,0%,100%,.05);border-bottom:1px solid hsla(0,0%,100%,.05);margin-top:-1px;margin-bottom:0;padding-left:10px;box-sizing:border-box}.consola-container .consola-wrap .method.method-info .method-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADISURBVChTY4ABp/AztmZBZ07qe538rO114rOa8+GTskYHbKHSEOARd6nLIOTsf61gIA46U6kVePYQiK3uc/K/hPG+LrCi8IyrtkZh5yCKgk/80w46ba0RdGYGhH/2v6rXyf88qtttGVwSLp2ECQLxeiAu1wo6uwpJ7L+o2f6TDA6xZz8jCyqFnuHXCj4djywmZXHoM/EK0azGqhBsNYpngL6VCTnGqRF4xgKo+D5IDO4ZEEAKnjcQBafvqwWf/YoSPDCAP8AZGAC7mLM81zgOTQAAAABJRU5ErkJggg==)}.consola-container .consola-wrap .method.method-warn{position:relative;display:flex;color:#ffdc9e;background-color:#332b00;border-top:1px solid #650;border-bottom:1px solid #650;margin-top:-1px;margin-bottom:1px;padding-left:10px;box-sizing:border-box}.consola-container .consola-wrap .method.method-warn .method-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACkSURBVChTbY7BCoJQFERn0Q/3BX1JuxQjsSCXiV8gtCgxhCIrKIRIqKDVzXl5w5cNHBjm6eGinXiAXu5inY2xYm/mbpIh+vcFhLA3sx0athNUhymEsP+10lAEEA17x8o/9wFuNGnYuVlWve0SQl7P0sBu3aq2R1Q/1JzSkYGd29eqNv2wjdnUuvNRciC/N+qe+7gidbA8zyHkOINsvA/sumcOkjcabcBmw2+mMgAAAABJRU5ErkJggg==)}.consola-container .consola-wrap .method.method-error{position:relative;display:flex;color:#ff8080;background-color:#290000;border-top:1px solid #5b0000;border-bottom:1px solid #5b0000;margin-top:-1px;margin-bottom:1px;padding-left:10px;box-sizing:border-box}.consola-container .consola-wrap .method.method-error .method-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADESURBVChTY4CB7ZI8tmfU5E6e01b+DMIgNkgMKg0BR9Vkux6YWPx/bemIgkFiIDmwogOaqrYPzazAEm8DwuGKYGyQHEgNw0VT05Mwib9v3v7/kJEHxiA2TDFIDcNNU4vPMFPACj58/P/v40cwGyYOUsNwy8IZRSFIEUgxskKQGoZrzp4ErQapYbgYHG371M4dLACTQGaD5EBqwD6/FpzQ9dTBE64IhkFiIDmwIhi4mlJqey8o4eR9r8jPIAxig8QgsgwMAFZz1YtGPXgjAAAAAElFTkSuQmCC)}.consola-container .consola-wrap .method.method-debug{position:relative;display:flex;color:#4d88ff;background-color:transparent;border-top:1px solid hsla(0,0%,100%,.05);border-bottom:1px solid hsla(0,0%,100%,.05);margin-top:-1px;margin-bottom:0;padding-left:10px;box-sizing:border-box}.consola-container .consola-wrap .method.method-debug .method-icon{background-image:url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 459 459'%3e%3cpath fill='%234D88FF' d='M433.5 127.5h-71.4a177.7 177.7 0 0 0-45.9-51L357 35.7 321.3 0l-56.1 56.1c-10.2-2.6-23-5.1-35.7-5.1s-25.5 2.5-35.7 5.1L137.7 0 102 35.7l40.8 40.8a177.7 177.7 0 0 0-45.9 51H25.5v51H79c-2.5 7.7-2.5 17.9-2.5 25.5v25.5h-51v51h51V306a88 88 0 0 0 2.5 25.5H25.5v51h71.4A152.2 152.2 0 0 0 229.5 459c56.1 0 107.1-30.6 132.6-76.5h71.4v-51H380c2.5-7.7 2.5-17.9 2.5-25.5v-25.5h51v-51h-51V204c0-7.7 0-17.9-2.5-25.5h53.5v-51zm-153 204h-102v-51h102v51zm0-102h-102v-51h102v51z'/%3e%3c/svg%3e")}.consola-container .consola-wrap .method *{vertical-align:top;box-sizing:border-box;font-family:Consolas,Lucida Console,Courier New,monospace;white-space:pre-wrap;font-size:12px}.consola-container .consola-wrap .method .method-icon{width:10px;height:18px;background-image:none;background-repeat:no-repeat;background-position:50% 50%}.consola-container .consola-wrap .method .method-text{clear:right;position:relative;padding:3px 22px 2px 0;margin-left:15px;min-height:18px;flex:auto;width:calc(100% - 15px)} diff --git a/docs/lab/css/normalize.css b/docs/lab/css/normalize.css deleted file mode 100644 index 9b77e0eb4..000000000 --- a/docs/lab/css/normalize.css +++ /dev/null @@ -1,461 +0,0 @@ -/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ - -/** - * 1. Change the default font family in all browsers (opinionated). - * 2. Correct the line height in all browsers. - * 3. Prevent adjustments of font size after orientation changes in - * IE on Windows Phone and in iOS. - */ - -/* Document - ========================================================================== */ - -html { - font-family: sans-serif; /* 1 */ - line-height: 1.15; /* 2 */ - -ms-text-size-adjust: 100%; /* 3 */ - -webkit-text-size-adjust: 100%; /* 3 */ -} - -/* Sections - ========================================================================== */ - -/** - * Remove the margin in all browsers (opinionated). - */ - -body { - margin: 0; -} - -/** - * Add the correct display in IE 9-. - */ - -article, -aside, -footer, -header, -nav, -section { - display: block; -} - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/* Grouping content - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - * 1. Add the correct display in IE. - */ - -figcaption, -figure, -main { /* 1 */ - display: block; -} - -/** - * Add the correct margin in IE 8. - */ - -figure { - margin: 1em 40px; -} - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ - -hr { - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/* Text-level semantics - ========================================================================== */ - -/** - * 1. Remove the gray background on active links in IE 10. - * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. - */ - -a { - background-color: transparent; /* 1 */ - -webkit-text-decoration-skip: objects; /* 2 */ -} - -/** - * Remove the outline on focused links when they are also active or hovered - * in all browsers (opinionated). - */ - -a:active, -a:hover { - outline-width: 0; -} - -/** - * 1. Remove the bottom border in Firefox 39-. - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} - -/** - * Prevent the duplicate application of `bolder` by the next rule in Safari 6. - */ - -b, -strong { - font-weight: inherit; -} - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { - font-weight: bolder; -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** - * Add the correct font style in Android 4.3-. - */ - -dfn { - font-style: italic; -} - -/** - * Add the correct background and color in IE 9-. - */ - -mark { - background-color: #ff0; - color: #000; -} - -/** - * Add the correct font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - */ - -audio, -video { - display: inline-block; -} - -/** - * Add the correct display in iOS 4-7. - */ - -audio:not([controls]) { - display: none; - height: 0; -} - -/** - * Remove the border on images inside links in IE 10-. - */ - -img { - border-style: none; -} - -/** - * Hide the overflow in IE. - */ - -svg:not(:root) { - overflow: hidden; -} - -/* Forms - ========================================================================== */ - -/** - * 1. Change the font styles in all browsers (opinionated). - * 2. Remove the margin in Firefox and Safari. - */ - -button, -input, -optgroup, -select, -textarea { - font-family: sans-serif; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ - -button, -input { /* 1 */ - overflow: visible; -} - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ - -button, -select { /* 1 */ - text-transform: none; -} - -/** - * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` - * controls in Android 4. - * 2. Correct the inability to style clickable types in iOS and Safari. - */ - -button, -html [type="button"], /* 1 */ -[type="reset"], -[type="submit"] { - -webkit-appearance: button; /* 2 */ -} - -/** - * Remove the inner border and padding in Firefox. - */ - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** - * Restore the focus styles unset by the previous rule. - */ - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** - * Change the border, margin, and padding in all browsers (opinionated). - */ - -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ - -legend { - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} - -/** - * 1. Add the correct display in IE 9-. - * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ - -progress { - display: inline-block; /* 1 */ - vertical-align: baseline; /* 2 */ -} - -/** - * Remove the default vertical scrollbar in IE. - */ - -textarea { - overflow: auto; -} - -/** - * 1. Add the correct box sizing in IE 10-. - * 2. Remove the padding in IE 10-. - */ - -[type="checkbox"], -[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ - -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** - * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. - */ - -[type="search"]::-webkit-search-cancel-button, -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* Interactive - ========================================================================== */ - -/* - * Add the correct display in IE 9-. - * 1. Add the correct display in Edge, IE, and Firefox. - */ - -details, /* 1 */ -menu { - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { - display: list-item; -} - -/* Scripting - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - */ - -canvas { - display: inline-block; -} - -/** - * Add the correct display in IE. - */ - -template { - display: none; -} - -/* Hidden - ========================================================================== */ - -/** - * Add the correct display in IE 10-. - */ - -[hidden] { - display: none; -} diff --git a/docs/lab/img/favicon.ico b/docs/lab/img/favicon.ico deleted file mode 100644 index 1b9f010e8b4a2cb5dc7d947b4d4b09b7b859b7ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9662 zcmeI2TdZ7D7{}M-sMEM~1gS$vT`JK&G)*cVq|vwp6(I-_iEC0N9$e#rlb$&a8X~Qr zX*T<0Z~Bjdac}{!w3&|2Z}~Rl^Q>V?TVrT_ zI;~%!KM!J^C}E4Phub`JyrRW5Z!6#qVC{C&KLw=Gz6hs)weP0>ks#LdCTXbCEiQ{z zp|CZneN~F_4Jmfr)W0E!r}^UPCK|q`-)JA;6&d_pWAFWRjd+?(V+Zg$&pWOETw*nX zcn{O}q_{puO#KI9|BLbQzMEPBH8;J?@-tidcG!Lxhx1S|?f8$=_jzLK$K0J}e7vvt zTVQL3?iJ)$f`dRTZNbpS_ibH^C7|=*W#X~*W9l9Xd~KY(_OJM@0egWwx*ug$obBkU zDf)Km$K8FL5+f^TDE}JY6$+9<%aMilvHiIz@v=mRp!mIuT&n3ga4_(snA$VM)@$v) z!EcTa6v@BsFqh3+BZeMHM4tewi)rgRIUei-+99o#--DPOMK&7qi4J0YgiojRPu5>_ zd|CRlJ1ri+|3du>ck}xFb<@dV%);qAkacc*Az#o*;l^j@4A*~zsioa!);^*a3vQ(HesB0`tK3X?qzNIC+YZDC(u5}^u65j zP1ZT5Js;i61+i>gwRO+@5NO}2vA25dCl3W_^cq5Io7cWOEkKS;>)Y!E@t;7idyZXm z_Mm?r*amz(U((K^cTjk{JbqSIfCBj<^w)t$z*As7SP6~Tn=U3PLOwbP`RXv(tc{B^9#zbpaSpV`o$A4M3xmC|kk$Em}+52M{j`Yhi(=*`3 z;AU`XmQjxURdlLF@2Fzv7L0uDt705-Z1gM@t|y-%J3YrIRB7I*9?jtx()-;Pz>*l_ z92>1aejL)c&T|Y+VkCdP*XSkr`$>bO7lDP~RAAS|+vw-fMwI^8wT|_gP9ougAgQj0 zt-#l{miAC!a~%B_jm^41Khs^^uk10@>8svn!FJ$}Z6@Xo>7z;GYHJPK_e4jUIm2T~ zeLL6!szC3&wIBc6dx-x32m1e?=+A-OdJgFH>Hi<;yk4%CrJpL-2jQhgIT>5cN&uf= zMW_r<^V-sBULPtaqsZ&C%Sj17REwX$<>Dg*heE~#kOUvo*dG^&?NNxfWc@+2Wxqr@=4ld+^e pBECAQ%@bdnRLkN?QXQ-{`Q+$ClPBXkE~uvCfa>HE&mGs6{!c$q56A!j 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/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",'
\n
\n
Console 0
\n
Clean
\n
\n
\n
'),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 From c9dfc883ab85e2a427cad8fe1a9e22d900d3f7c5 Mon Sep 17 00:00:00 2001 From: Harvey Zack Date: Thu, 6 Dec 2018 15:39:47 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=AE=8C=E6=88=90gif=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/gif/css/common.css | 4 +- docs/gif/js/common.js | 16 +-- docs/lab/js/example.js | 9 +- docs/uncompiled/artplayer-plugin-gif.js | 129 ++++++++++++++++- docs/uncompiled/artplayer.css | 12 +- docs/uncompiled/artplayer.js | 20 +-- packages/artplayer-plugin-gif/README.md | 62 +++++++- packages/artplayer-plugin-gif/src/index.js | 132 +++++++++++++++++- .../artplayer/src/player/screenshotMix.js | 10 +- packages/artplayer/src/style/bottom.scss | 2 + packages/artplayer/src/utils/index.js | 10 ++ 11 files changed, 352 insertions(+), 54 deletions(-) diff --git a/docs/gif/css/common.css b/docs/gif/css/common.css index 6851a54e2..4e7c98a94 100644 --- a/docs/gif/css/common.css +++ b/docs/gif/css/common.css @@ -38,12 +38,14 @@ html, body, .container { } .part-right .preview { + display: flex; + align-items: center; + justify-content: center; width: 500px; height: 500px; background: #f3f3f3; } .part-right .preview img { - width: 100%; max-width: 100%; } \ No newline at end of file diff --git a/docs/gif/js/common.js b/docs/gif/js/common.js index 75e6bb828..cc522d489 100644 --- a/docs/gif/js/common.js +++ b/docs/gif/js/common.js @@ -17,24 +17,18 @@ 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: '打开' + html: '打开', + mounted: $preview => { + art.plugins.localPreview.attach($preview); + }, }, ], plugins: [artplayerPluginGif] }); - - art.on('firstCanplay', () => { - var preview = document.querySelector('.art-control-preview'); - art.plugins.localPreview.attach(preview); - art.plugins.artplayerPluginGif.create({}, image => { - var animatedImage = document.createElement('img'); - animatedImage.src = image; - document.querySelector('.preview').appendChild(animatedImage); - }); - }); })(); diff --git a/docs/lab/js/example.js b/docs/lab/js/example.js index 5f2a52985..47d70a941 100644 --- a/docs/lab/js/example.js +++ b/docs/lab/js/example.js @@ -98,8 +98,10 @@ var art = new Artplayer({ { name: 'preview', position: 'right', - index: 1, - html: '打开' + html: '打开', + mounted: $preview => { + art.plugins.localPreview.attach($preview); + }, }, ], icons: { @@ -108,9 +110,6 @@ var art = new Artplayer({ }); art.on('firstCanplay', () => { - var preview = document.querySelector('.art-control-preview'); - art.plugins.localPreview.attach(preview); - art.on('resize', $player => { art.controls.quality.show(); art.controls.setting.show(); diff --git a/docs/uncompiled/artplayer-plugin-gif.js b/docs/uncompiled/artplayer-plugin-gif.js index 51a6e480b..1600412c3 100644 --- a/docs/uncompiled/artplayer-plugin-gif.js +++ b/docs/uncompiled/artplayer-plugin-gif.js @@ -2719,22 +2719,137 @@ }, 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, { - video: [art.template.$video.src] + offset: player.currentTime, + video: [$video.src] }), function (obj) { - if (!obj.error) { - if (callback) { - callback(obj.image); - } - } else { - console.log(obj.error); + 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'); }); } }; 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/README.md b/packages/artplayer-plugin-gif/README.md index 543c3a97a..c4e27d982 100644 --- a/packages/artplayer-plugin-gif/README.md +++ b/packages/artplayer-plugin-gif/README.md @@ -1,3 +1,63 @@ # artplayer-plugin-gif -Gif plugin for ArtPlayer +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/src/index.js b/packages/artplayer-plugin-gif/src/index.js index 766cc4557..cb81ea917 100644 --- a/packages/artplayer-plugin-gif/src/index.js +++ b/packages/artplayer-plugin-gif/src/index.js @@ -1,22 +1,140 @@ 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, - video: [art.template.$video.src], + offset: player.currentTime, + video: [$video.src], }, obj => { - if (!obj.error) { - if (callback) { - callback(obj.image); - } - } else { - console.log(obj.error); + 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'); }, ); }, 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