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.
This commit is contained in:
Emmanuel Gil Peyrot 2018-10-28 18:58:23 +01:00 committed by JC Brand
parent 352c0797ad
commit ab5dd4a146
9 changed files with 66 additions and 60 deletions

View File

@ -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); }

77
dist/converse.js vendored
View File

@ -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) +

View File

@ -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);
}

View File

@ -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();
image = this.model.vcard.get('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;
canvas_el.outerHTML = tpl_avatar({
'classes': canvas_el.getAttribute('class'),
'width': canvas_el.width,
'height': canvas_el.height,
'image': "data:" + image_type + ";base64," + image,
});
},
};

View File

@ -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
src/templates/avatar.svg Normal file
View File

@ -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>

After

Width:  |  Height:  |  Size: 283 B

View File

@ -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">

View File

@ -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>

View File

@ -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',