<h1>SVG</h1>
<wc-svg-graph points='[[10,10], [20,20], [30,30], [40,40], [10,20,"blue"], [20,30,"blue"], [70,95,"blue"],[-20,-20,"magenta", 5, "square"]]'></wc-svg-graph>
<wc-svg-graph points='[[10,10], [20,20], [30,30], [40,40], [10,20,"blue"], [20,30,"blue"], [70,95,"blue"]]'>
</wc-svg-graph>
<wc-svg-graph id="test1"></wc-svg-graph>
<script>
document.addEventListener("DOMContentLoaded", () => {
document.querySelector("#test1").points = [[-10, -10, "green"], [-20, -20, "green"], [-30, -30, "green"], [-40, -40, "green"]];
});
</script>
<wc-svg-graph func="return 75 * Math.sin(5 * x)" xmin="-3" xmax="3" step="0.01">
</wc-svg-graph>
<wc-svg-graph func="return 75 * Math.sin(5 * x)" xmin="-3" xmax="3" step="0.05">
</wc-svg-graph>
<wc-svg-graph func="return 75 * Math.sin(5 * x)" xmin="-3" xmax="3" step="0.01" width="1280" height="720" continuous default-size="4" default-color="blue" thickness="2">
</wc-svg-graph>
function windowValue(v, vmin, vmax, flipped = false) {
v = flipped ? -v : v;
return (v - vmin) / (vmax - vmin);
}
function hyphenCaseToCamelCase(text) {
return text.replace(/-([a-z])/g, g => g[1].toUpperCase());
}
function createShape(shape, [x, y], size, color){
switch(shape){
case "circle": {
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", x);
circle.setAttribute("cy", y);
circle.setAttribute("r", size);
circle.setAttribute("fill", color);
return circle;
}
case "square": {
const halfSize = size / 2;
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", x - halfSize);
rect.setAttribute("y", y - halfSize);
rect.setAttribute("width", size);
rect.setAttribute("height", size);
rect.setAttribute("fill", color);
return rect;
}
}
}
class WcSvgGraph extends HTMLElement {
#points = [];
#width = 320;
#height = 240;
#xmax = 100;
#xmin = -100;
#ymax = 100;
#ymin = -100;
#func;
#step = 1;
#thickness = 1;
#continuous = false;
#defaultShape = "circle";
#defaultSize = 2;
#defaultColor = "#F00"
static observedAttributes = ["points", "func", "step", "width", "height", "xmin", "xmax", "ymin", "ymax", "default-shape", "default-size", "default-color", "continuous", "thickness"];
constructor() {
super();
this.bind(this);
}
bind(element) {
element.attachEvents.bind(element);
}
render() {
if(!this.shadowRoot){
this.attachShadow({ mode: "open" });
}
this.shadowRoot.innerHTML = "";
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", this.#width);
svg.setAttribute("height", this.#height);
const background = document.createElementNS("http://www.w3.org/2000/svg", "rect");
background.setAttribute("width", this.#width);
background.setAttribute("height", this.#height);
background.setAttribute("fill", "white");
svg.appendChild(background);
const guides = document.createElementNS("http://www.w3.org/2000/svg", "path");
guides.setAttribute("stroke-width", 1.0);
guides.setAttribute("stroke", "black");
guides.setAttribute("d", `M0,${this.#height / 2} H${this.#width} M${this.#width / 2},0 V${this.#height}`);
svg.appendChild(guides);
let points;
if(this.#func){
points = [];
for (let x = this.#xmin; x < this.#xmax; x += this.#step) {
const y = this.#func(x);
points.push({ x, y, color: this.#defaultColor, size: this.#defaultSize, shape: this.#defaultShape});
}
} else {
points = this.#points;
}
points = points.map(p => ({
x: windowValue(p.x, this.#xmin, this.#xmax) * this.#width,
y: windowValue(p.y, this.#ymin, this.#ymax, true) * this.#height,
color: p.color,
size: p.size,
shape: p.shape
}));
if(this.#continuous){
const pathData = ["M"];
pathData.push(points[0].x.toFixed(2), points[0].y.toFixed(2));
for (let i = 1; i < points.length; i++) {
pathData.push("L", points[i].x.toFixed(2), points[i].y.toFixed(2));
}
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("fill", "none");
path.setAttribute("stroke-width", this.#thickness);
path.setAttribute("stroke", this.#defaultColor);
path.setAttribute("d", pathData.join(" "));
svg.appendChild(path);
}
for(const point of points){
const shape = createShape(
point.shape,
[point.x, point.y],
point.size,
point.color
);
svg.appendChild(shape);
}
this.shadowRoot.appendChild(svg);
}
attachEvents() {
}
connectedCallback() {
this.render();
this.attachEvents();
}
attributeChangedCallback(name, oldValue, newValue) {
this[hyphenCaseToCamelCase(name)] = newValue;
}
set points(value) {
if(typeof(value) === "string"){
value = JSON.parse(value);
}
value = value.map(p => ({
x: p[0],
y: p[1],
color: p[2] ?? this.#defaultColor,
size: p[3] ?? this.#defaultSize,
shape: p[4] ?? this.#defaultShape
}));
this.#points = value;
this.render();
}
get points() {
return this.#points;
}
set width(value) {
this.#width = parseFloat(value);
}
get width() {
return this.#width;
}
set height(value) {
this.#height = parseFloat(value);
}
get height() {
return this.#height;
}
set xmax(value) {
this.#xmax = parseFloat(value);
}
get xmax() {
return this.#xmax;
}
set xmin(value) {
this.#xmin = parseFloat(value);
}
get xmin() {
return this.#xmin;
}
set ymax(value) {
this.#ymax = parseFloat(value);
}
get ymax() {
return this.#ymax;
}
set ymin(value) {
this.#ymin = parseFloat(value);
}
get ymin() {
return this.#ymin;
}
set func(value) {
this.#func = new Function(["x"], value);
this.render();
}
set step(value) {
this.#step = parseFloat(value);
}
set defaultSize(value){
this.#defaultSize = parseFloat(value);
}
set defaultShape(value) {
this.#defaultShape = value;
}
set defaultColor(value) {
this.#defaultColor = value;
}
set continuous(value){
this.#continuous = value !== undefined;
}
set thickness(value){
this.#thickness = parseFloat(value);
}
}
customElements.define("wc-svg-graph", WcSvgGraph);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.