Browse Source

Switch avatar rendering from canvas to SVG.

This delegates the calculation of the aspect ratio to the browser, and
generally simplifies the code.

Fixes #1156.
Emmanuel Gil Peyrot 6 năm trước cách đây
mục cha
commit
ab5dd4a146

+ 2 - 2
css/converse.css

@@ -9631,7 +9631,7 @@ body.reset {
   #conversejs div, #conversejs span, #conversejs h1, #conversejs h2, #conversejs h3, #conversejs h4, #conversejs h5, #conversejs h6, #conversejs p, #conversejs blockquote,
   #conversejs pre, #conversejs a, #conversejs em, #conversejs img, #conversejs strong, #conversejs dl, #conversejs dt, #conversejs dd, #conversejs ol, #conversejs ul, #conversejs li,
   #conversejs fieldset, #conversejs form, #conversejs legend, #conversejs table, #conversejs caption, #conversejs tbody,
-  #conversejs tfoot, #conversejs thead, #conversejs tr, #conversejs th, #conversejs td, #conversejs article, #conversejs aside, #conversejs canvas, #conversejs details,
+  #conversejs tfoot, #conversejs thead, #conversejs tr, #conversejs th, #conversejs td, #conversejs article, #conversejs aside, #conversejs details,
   #conversejs embed, #conversejs figure, #conversejs figcaption, #conversejs footer, #conversejs header, #conversejs hgroup, #conversejs menu,
   #conversejs nav, #conversejs output, #conversejs ruby, #conversejs section, #conversejs summary, #conversejs time, #conversejs mark, #conversejs audio, #conversejs video {
     margin: 0;
@@ -9669,7 +9669,7 @@ body.reset {
       color: var(--subdued-color); }
       #conversejs a.fa:hover, #conversejs a.far:hover, #conversejs a.fas:hover, #conversejs a:visited.fa:hover, #conversejs a:visited.far:hover, #conversejs a:visited.fas:hover, #conversejs a:not([href]):not([tabindex]).fa:hover, #conversejs a:not([href]):not([tabindex]).far:hover, #conversejs a:not([href]):not([tabindex]).fas:hover {
         color: var(--gray-color); }
-  #conversejs canvas {
+  #conversejs svg {
     border-radius: var(--chatbox-border-radius); }
   #conversejs .fa, #conversejs .far, #conversejs .fas {
     color: var(--subdued-color); }

+ 46 - 31
dist/converse.js

@@ -58492,8 +58492,10 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var backbone_overview__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! backbone.overview */ "./node_modules/backbone.overview/backbone.overview.js");
 /* harmony import */ var backbone_overview__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(backbone_overview__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
-/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! templates/chatboxes.html */ "./src/templates/chatboxes.html");
-/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4__);
+/* harmony import */ var templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! templates/avatar.svg */ "./src/templates/avatar.svg");
+/* harmony import */ var templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4__);
+/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/chatboxes.html */ "./src/templates/chatboxes.html");
+/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5__);
 // Converse.js
 // http://conversejs.org
 //
@@ -58504,6 +58506,7 @@ __webpack_require__.r(__webpack_exports__);
 
 
 
+
 const _converse$env = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].env,
       Backbone = _converse$env.Backbone,
       _ = _converse$env._,
@@ -58519,27 +58522,12 @@ const AvatarMixin = {
     }
 
     const image_type = this.model.vcard.get('image_type'),
-          image = this.model.vcard.get('image'),
-          img_src = "data:" + image_type + ";base64," + image,
-          img = new Image();
-    return new Promise((resolve, reject) => {
-      img.onload = () => {
-        const ctx = canvas_el.getContext('2d'),
-              ratio = img.width / img.height;
-        ctx.clearRect(0, 0, canvas_el.width, canvas_el.height);
-
-        if (ratio < 1) {
-          const scaled_img_with = canvas_el.width * ratio,
-                x = Math.floor((canvas_el.width - scaled_img_with) / 2);
-          ctx.drawImage(img, x, 0, scaled_img_with, canvas_el.height);
-        } else {
-          ctx.drawImage(img, 0, 0, canvas_el.width, canvas_el.height * ratio);
-        }
-
-        resolve();
-      };
-
-      img.src = img_src;
+          image = this.model.vcard.get('image');
+    canvas_el.outerHTML = templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4___default()({
+      'classes': canvas_el.getAttribute('class'),
+      'width': canvas_el.width,
+      'height': canvas_el.height,
+      'image': "data:" + image_type + ";base64," + image
     });
   }
 
@@ -58618,11 +58606,11 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
 
       render() {
         try {
-          this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4___default()();
+          this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5___default()();
         } catch (e) {
           this._ensureElement();
 
-          this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4___default()();
+          this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5___default()();
         }
 
         this.row_el = this.el.querySelector('.row');
@@ -61766,14 +61754,13 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
           msg_content.innerHTML = _.flow(_.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].geoUriToHttp, _, _converse.geouri_replacement), _.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addMentionsMarkup, _, this.model.get('references'), this.model.collection.chatbox), utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addHyperlinks, utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].renderNewLines, _.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addEmoji, _converse, _))(text);
         }
 
-        const promises = [];
-        promises.push(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].renderImageURLs(_converse, msg_content));
+        const promise = utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].renderImageURLs(_converse, msg_content);
 
         if (this.model.get('type') !== 'headline') {
-          promises.push(this.renderAvatar(msg));
+          this.renderAvatar(msg);
         }
 
-        await Promise.all(promises);
+        await promise;
         this.replaceElement(msg);
         this.model.collection.trigger('rendered', this);
       },
@@ -100974,6 +100961,34 @@ return __p
 
 /***/ }),
 
+/***/ "./src/templates/avatar.svg":
+/*!**********************************!*\
+  !*** ./src/templates/avatar.svg ***!
+  \**********************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./node_modules/lodash/escape.js")};
+module.exports = function(o) {
+var __t, __p = '', __e = _.escape;
+__p += '<!-- src/templates/avatar.svg -->\n<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="' +
+__e(o.classes) +
+'" width="' +
+__e(o.width) +
+'" height="' +
+__e(o.height) +
+'">\n    <image width="' +
+__e(o.width) +
+'" height="' +
+__e(o.height) +
+'" preserveAspectRatio="xMidYMid meet" xlink:href="' +
+__e(o.image) +
+'"/>\n</svg>\n';
+return __p
+};
+
+/***/ }),
+
 /***/ "./src/templates/bookmark.html":
 /*!*************************************!*\
   !*** ./src/templates/bookmark.html ***!
@@ -102892,7 +102907,7 @@ __e(o.image) +
  } ;
 __p += '\n                                        ';
  if (!o.image) { ;
-__p += '\n                                            <canvas class="avatar" height="100px" width="100px"/>\n                                        ';
+__p += '\n                                            <canvas class="avatar" height="100px" width="100px"></canvas>\n                                        ';
  } ;
 __p += '\n                                    </a>\n                                    <input class="hidden" name="image" type="file">\n                                </div>\n                                <div class="col">\n                                    <div class="form-group">\n                                        <label class="col-form-label">' +
 __e(o.label_jid) +
@@ -102989,7 +103004,7 @@ var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./no
 module.exports = function(o) {
 var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
 function print() { __p += __j.call(arguments, '') }
-__p += '<!-- src/templates/profile_view.html -->\n<div class="userinfo controlbox-padded">\n<div class="controlbox-section profile d-flex">\n    <a class="show-profile" href="#">\n        <canvas alt="o.__(\'Your avatar\')" class="avatar align-self-center" height="40" width="40"></canvas>\n    </a>\n    <span class="username w-100 align-self-center">' +
+__p += '<!-- src/templates/profile_view.html -->\n<div class="userinfo controlbox-padded">\n<div class="controlbox-section profile d-flex">\n    <a class="show-profile" href="#">\n        <canvas class="avatar align-self-center" height="40" width="40"></canvas>\n    </a>\n    <span class="username w-100 align-self-center">' +
 __e(o.fullname) +
 '</span>\n    <a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="' +
 __e(o.info_details) +

+ 2 - 2
sass/_core.scss

@@ -196,7 +196,7 @@ body.reset {
     div, span, h1, h2, h3, h4, h5, h6, p, blockquote,
     pre, a, em, img, strong, dl, dt, dd, ol, ul, li,
     fieldset, form, legend, table, caption, tbody,
-    tfoot, thead, tr, th, td, article, aside, canvas, details,
+    tfoot, thead, tr, th, td, article, aside, details,
     embed, figure, figcaption, footer, header, hgroup, menu,
     nav, output, ruby, section, summary, time, mark, audio, video {
         margin: 0;
@@ -254,7 +254,7 @@ body.reset {
         }
     }
 
-    canvas {
+    svg {
         border-radius: var(--chatbox-border-radius);
     }
 

+ 8 - 19
src/converse-chatboxviews.js

@@ -8,6 +8,7 @@ import "@converse/headless/converse-chatboxes";
 import "backbone.nativeview";
 import "backbone.overview";
 import converse from "@converse/headless/converse-core";
+import tpl_avatar from "templates/avatar.svg";
 import tpl_chatboxes from "templates/chatboxes.html";
 
 const { Backbone, _, utils } = converse.env;
@@ -22,25 +23,13 @@ const AvatarMixin = {
             return;
         }
         const image_type = this.model.vcard.get('image_type'),
-                image = this.model.vcard.get('image'),
-                img_src = "data:" + image_type + ";base64," + image,
-                img = new Image();
-
-        return new Promise((resolve, reject) => {
-            img.onload = () => {
-                const ctx = canvas_el.getContext('2d'),
-                        ratio = img.width / img.height;
-                ctx.clearRect(0, 0, canvas_el.width, canvas_el.height);
-                if (ratio < 1) {
-                    const scaled_img_with = canvas_el.width*ratio,
-                            x = Math.floor((canvas_el.width-scaled_img_with)/2);
-                    ctx.drawImage(img, x, 0, scaled_img_with, canvas_el.height);
-                } else {
-                    ctx.drawImage(img, 0, 0, canvas_el.width, canvas_el.height*ratio);
-                }
-                resolve();
-            };
-            img.src = img_src;
+                image = this.model.vcard.get('image');
+
+        canvas_el.outerHTML = tpl_avatar({
+            'classes': canvas_el.getAttribute('class'),
+            'width': canvas_el.width,
+            'height': canvas_el.height,
+            'image': "data:" + image_type + ";base64," + image,
         });
     },
 };

+ 3 - 4
src/converse-message-view.js

@@ -155,12 +155,11 @@ converse.plugins.add('converse-message-view', {
                         _.partial(u.addEmoji, _converse, _)
                     )(text);
                 }
-                const promises = [];
-                promises.push(u.renderImageURLs(_converse, msg_content));
+                const promise = u.renderImageURLs(_converse, msg_content);
                 if (this.model.get('type') !== 'headline') {
-                    promises.push(this.renderAvatar(msg));
+                    this.renderAvatar(msg);
                 }
-                await Promise.all(promises);
+                await promise;
                 this.replaceElement(msg);
                 this.model.collection.trigger('rendered', this);
             },

+ 3 - 0
src/templates/avatar.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="{{{o.classes}}}" width="{{{o.width}}}" height="{{{o.height}}}">
+    <image width="{{{o.width}}}" height="{{{o.height}}}" preserveAspectRatio="xMidYMid meet" xlink:href="{{{o.image}}}"/>
+</svg>

+ 1 - 1
src/templates/profile_modal.html

@@ -26,7 +26,7 @@
                                             <img alt="{{{o.alt_avatar}}}" class="img-thumbnail avatar align-self-center" height="100px" width="100px" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
                                         {[ } ]}
                                         {[ if (!o.image) { ]}
-                                            <canvas class="avatar" height="100px" width="100px"/>
+                                            <canvas class="avatar" height="100px" width="100px"></canvas>
                                         {[ } ]}
                                     </a>
                                     <input class="hidden" name="image" type="file">

+ 1 - 1
src/templates/profile_view.html

@@ -1,7 +1,7 @@
 <div class="userinfo controlbox-padded">
 <div class="controlbox-section profile d-flex">
     <a class="show-profile" href="#">
-        <canvas alt="o.__('Your avatar')" class="avatar align-self-center" height="40" width="40"></canvas>
+        <canvas class="avatar align-self-center" height="40" width="40"></canvas>
     </a>
     <span class="username w-100 align-self-center">{{{o.fullname}}}</span>
     <a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="{{{o.info_details}}}"></a>

+ 1 - 1
webpack.config.js

@@ -36,7 +36,7 @@ const config = {
             use: "exports-loader?filterXSS,filterCSS"
         },
         {
-            test: /\.html$/,
+            test: /\.(html|svg)$/,
             exclude: /node_modules/,
             use: [{
                 loader: 'lodash-template-webpack-loader',