d70463cfba
Todo for first usable version: Add level customization per levelset (course)
212 lines
9.7 KiB
JavaScript
212 lines
9.7 KiB
JavaScript
// 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;
|
|
}); |