moodle-block_mytreestudyplan/amd/src/renderbadge.js

212 lines
9.7 KiB
JavaScript
Raw Normal View History

// Put this file in path/to/plugin/amd/src
// You can call it anything you like
define(['jquery', 'core/str', 'core/ajax'], function ($, str, ajax) {
let self = {
init: function init() {
// Put whatever you like here. $ is available
// to you as normal.
$("figure.levelbadge").each(function () {
let props = self.fetchProperties(this);
let $this = $(this);
let $canvas = $("<canvas height= '"+props.height +"' width = '"+props.width+"'/>");
console.info("$canvas", $canvas, $canvas[0]);
$('canvas', this).remove();// clear out any existing canvas
$this.append($canvas); // Put the canvas in there
self.render($canvas[0], props);
});
},
render: function render(canvas, props) {
let ctx = canvas.getContext("2d");
// Color configuration
let colors = {
base: props.color,
light: self.shadeBlendConvert(0.6, props.color), // 60% lighter
dark: self.shadeBlendConvert(0.3, props.color), // 30% lighter
lightPoint: self.shadeBlendConvert(0.8, props.color), // 80% lighter
reflection: {
lightest: "#ffffff51",
darkest: "#ffffff20",
},
radialGradient: {
x0: 0.75,
y0: 0.25,
r0: 0.05,
x1: 0.6,
y1: 0.4,
r1: 0.4,
},
levelText: "white",
};
// size and position configuration
let config = {
size: Math.min(props.height, props.width),
borderWidth: 0.05, // factor of image size
reflection: {
angle: -20, // relative to horizontal
offset: 0.125, // relative to radius
},
levelText: {
x: 0.5,
y: 0.9,
size: 0.2,
font: "Open Sans, Arial, helvetica, sans-serif",
},
icon: {
x: 0.5,
y: 0.47,
scale: 0.7, // scale to this fraction of image size
}
}
console.info("Config", config);
console.info("Colors", colors);
console.info("Props", props);
// draw main circle
let baseGradient = ctx.createRadialGradient(
config.size * colors.radialGradient.x0,
config.size * colors.radialGradient.y0,
(config.size ) * colors.radialGradient.r0,
config.size * colors.radialGradient.x1,
config.size * colors.radialGradient.y1,
(config.size ) * colors.radialGradient.r1,
);
baseGradient.addColorStop(0, colors.lightPoint);
baseGradient.addColorStop(1, colors.base);
ctx.beginPath();
ctx.fillStyle = baseGradient;
ctx.arc(0.5 * config.size, 0.5 * config.size, config.size / 2, 0, 2 * Math.PI);
ctx.fill();
// draw main reflection
let rflOffset = Math.asin(config.reflection.offset);
let rflAngleRad = (config.reflection.angle / 360.0) * 2 * Math.PI;
let rflGradient = ctx.createLinearGradient(
(0.5 - config.reflection.offset * Math.sin(rflAngleRad))/2 * config.size,
(0.5 + config.reflection.offset * Math.cos(rflAngleRad))/2 * config.size,
Math.sin(rflAngleRad)/2 * config.size,
Math.cos(rflAngleRad)/2 * config.size
);
rflGradient.addColorStop(0, colors.reflection.lightest);
rflGradient.addColorStop(1, colors.reflection.darkest);
ctx.beginPath();
ctx.fillStyle = rflGradient;
ctx.arc(0.5 * config.size, 0.5 * config.size, config.size / 2,
0 + rflOffset + rflAngleRad,
Math.PI - rflOffset + rflAngleRad);
ctx.fill();
// draw empty border
let strokeWidth = config.size * config.borderWidth;
ctx.beginPath();
ctx.strokeStyle = colors.light;
ctx.lineWidth = strokeWidth;
ctx.arc(0.5 * config.size, 0.5 * config.size, config.size / 2 - strokeWidth / 2, 0, 2 * Math.PI);
ctx.stroke();
// draw current progress border
ctx.beginPath();
ctx.strokeStyle = colors.dark;
ctx.lineWidth = strokeWidth;
ctx.arc(0.5 * config.size, 0.5 * config.size, config.size / 2 - strokeWidth / 2,
0 - Math.PI / 2, // -90 degrees (top)
props.progress * 2 * Math.PI - Math.PI / 2 // fraction of whole circle offset with -90 degrees
);
ctx.stroke();
if (props.level) {
// write level in lower part
ctx.font = "" + config.size * config.levelText.size + "px " + config.levelText.font;
ctx.fillStyle = colors.levelText;
ctx.textAlign = "center";
ctx.fillText("" + props.level, config.size * config.levelText.x, config.size * config.levelText.y);
}
/*
var imageObj = new Image();
imageObj.onload = function () {
ctx.drawImage(this, 15, 15, 120, 120);
};
imageObj.src = "https://dev.miqra.nl/blocks/gradelevel/pix/undefinedskill.svg";
*/
// paint image in center
if (props.image) {
let iconImg = new Image();
iconImg.onload = function () {
let imPos = {
x: 0, // to be determined later
y: 0, // to be determined later
w: config.size * config.icon.scale,
h: config.size * config.icon.scale,
};
// preserve aspect ratio
if (this.width > this.height) {
imPos.h *= this.height / this.width;
} else {
imPos.w *= this.width / this.height;
}
// calculate x and y
imPos.x = (config.size * config.icon.x) - (imPos.w / 2);
imPos.y = (config.size * config.icon.y) - (imPos.h / 2);
ctx.drawImage(this, 15, 15, 120, 120);//imPos.x, imPos.y, imPos.w, imPos.h);
}
console.info("Image: ",props.image);
iconImg.src = props.image;
}
// complete
},
fetchProperties: function fetchProperties(figure) {
let $figure = $(figure);
let $image = $figure.find("img");
let image = null;
if ($image.length > 0) {
image = $image.attr("src");
}
return {
progress: $figure.attr("data-progress"),
width: $figure.attr("data-width"),
height: $figure.attr("data-height"),
color: $figure.attr("data-color"),
level: $figure.attr("data-level"),
image: image,
};
},
shadeBlendConvert: function shadeBlendConvert(p, from, to) {
// Code for this function was taken from https://github.com/PimpTrizkit/PJs pSBC.js
if (typeof (p) != "number" || p < -1 || p > 1 || typeof (from) != "string" || (from[0] != 'r' && from[0] != '#') || (to && typeof (to) != "string")) return null; //ErrorCheck
if (!this.sbcRip) this.sbcRip = (d) => {
let l = d.length, RGB = {};
if (l > 9) {
d = d.split(",");
if (d.length < 3 || d.length > 4) return null;//ErrorCheck
RGB[0] = i(d[0].split("(")[1]), RGB[1] = i(d[1]), RGB[2] = i(d[2]), RGB[3] = d[3] ? parseFloat(d[3]) : -1;
} else {
if (l == 8 || l == 6 || l < 4) return null; //ErrorCheck
if (l < 6) d = "#" + d[1] + d[1] + d[2] + d[2] + d[3] + d[3] + (l > 4 ? d[4] + "" + d[4] : ""); //3 or 4 digit
d = i(d.slice(1), 16), RGB[0] = d >> 16 & 255, RGB[1] = d >> 8 & 255, RGB[2] = d & 255, RGB[3] = -1;
if (l == 9 || l == 5) RGB[3] = r((RGB[2] / 255) * 10000) / 10000, RGB[2] = RGB[1], RGB[1] = RGB[0], RGB[0] = d >> 24 & 255;
}
return RGB;
}
var i = parseInt, r = Math.round, h = from.length > 9, h = typeof (to) == "string" ? to.length > 9 ? true : to == "c" ? !h : false : h, b = p < 0, p = b ? p * -1 : p, to = to && to != "c" ? to : b ? "#000000" : "#FFFFFF", f = this.sbcRip(from), t = this.sbcRip(to);
if (!f || !t) return null; //ErrorCheck
if (h) return "rgb" + (f[3] > -1 || t[3] > -1 ? "a(" : "(") + r((t[0] - f[0]) * p + f[0]) + "," + r((t[1] - f[1]) * p + f[1]) + "," + r((t[2] - f[2]) * p + f[2]) + (f[3] < 0 && t[3] < 0 ? ")" : "," + (f[3] > -1 && t[3] > -1 ? r(((t[3] - f[3]) * p + f[3]) * 10000) / 10000 : t[3] < 0 ? f[3] : t[3]) + ")");
else return "#" + (0x100000000 + r((t[0] - f[0]) * p + f[0]) * 0x1000000 + r((t[1] - f[1]) * p + f[1]) * 0x10000 + r((t[2] - f[2]) * p + f[2]) * 0x100 + (f[3] > -1 && t[3] > -1 ? r(((t[3] - f[3]) * p + f[3]) * 255) : t[3] > -1 ? r(t[3] * 255) : f[3] > -1 ? r(f[3] * 255) : 255)).toString(16).slice(1, f[3] > -1 || t[3] > -1 ? undefined : -2);
},
};
return self;
});