From f5ea3a8fa47349e30f695d779d9cbb0faeace412 Mon Sep 17 00:00:00 2001 From: SArpnt Date: Tue, 18 Feb 2025 14:13:39 -0500 Subject: [PATCH 1/7] use same font stack as snap --- snap/style.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/style.js b/snap/style.js index b80718b0..87155c09 100644 --- a/snap/style.js +++ b/snap/style.js @@ -2094,6 +2094,6 @@ export default class Style { * @constant */ static get defaultFontFamily() { - return "Lucida Grande, Verdana, Arial, DejaVu Sans, sans-serif" + return "Verdana, sans-serif" } } From 3a70a65ec6f5d9bb40a90ec797227f90e0590c9e Mon Sep 17 00:00:00 2001 From: SArpnt Date: Wed, 5 Mar 2025 09:53:34 -0500 Subject: [PATCH 2/7] simpler scratch 2 dark/light filter fits in better with snap block changes in later commits --- scratch2/style.js | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/scratch2/style.js b/scratch2/style.js index 30bb49a7..4268a58e 100644 --- a/scratch2/style.js +++ b/scratch2/style.js @@ -1341,25 +1341,31 @@ export default class Style { } static darkFilter(id) { - const f = new Filter(id) - - f.merge([ - "SourceGraphic", - f.comp("in", f.flood("#000", 0.2), "SourceAlpha"), - ]) - - return f.el + return SVG.withChildren( + SVG.el("filter", { id, x: "0", y: "0", width: "1", height: "1" }), + [ + SVG.el("feColorMatrix", { type: "matrix", values: ` + .8 0 0 0 0 + 0 .8 0 0 0 + 0 0 .8 0 0 + 0 0 0 1 0 + ` }), + ], + ) } static lightFilter(id) { - const f = new Filter(id) - - f.merge([ - "SourceGraphic", - f.comp("in", f.flood("#fff", 0.4), "SourceAlpha"), - ]) - - return f.el + return SVG.withChildren( + SVG.el("filter", { id, x: "0", y: "0", width: "1", height: "1" }), + [ + SVG.el("feColorMatrix", { type: "matrix", values: ` + .6 0 0 0 .4 + 0 .6 0 0 .4 + 0 0 .6 0 .4 + 0 0 0 1 0 + ` }), + ], + ) } static darkRect(w, h, color, el) { From 2fde7d1e7781e288fb3f4572e52085e9d69fce47 Mon Sep 17 00:00:00 2001 From: SArpnt Date: Wed, 5 Mar 2025 11:33:28 -0500 Subject: [PATCH 3/7] simpler scratch 3 zebra filter fits in better with snap block changes in later commits --- scratch3/style.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/scratch3/style.js b/scratch3/style.js index d99f19c5..9fcde3c8 100644 --- a/scratch3/style.js +++ b/scratch3/style.js @@ -1,7 +1,6 @@ import SVG from "./draw.js" import Color from "../shared/color.js" import cssContent from "./style.css.js" -import Filter from "./filter.js" // Need to define here, as we cannot reference Style#makeNewIcons // during JS loading phase. @@ -2339,13 +2338,16 @@ export default class Style { } static zebraFilter(id, isDark) { - const f = new Filter(id) - - f.merge([ - "SourceGraphic", - f.comp("in", f.flood(isDark ? "#000" : "#fff", 0.3), "SourceAlpha"), - ]) - - return f.el + return SVG.withChildren( + SVG.el("filter", { id, x: "0", y: "0", width: "1", height: "1" }), + [ + SVG.el("feColorMatrix", { type: "matrix", values: ` + .7 0 0 0 ${isDark ? 0 : .3} + 0 .7 0 0 ${isDark ? 0 : .3} + 0 0 .7 0 ${isDark ? 0 : .3} + 0 0 0 1 0 + ` }), + ], + ) } } From 735cad5fd84a4c6845c3d1e254cae53d8e43487a Mon Sep 17 00:00:00 2001 From: SArpnt Date: Wed, 5 Mar 2025 12:18:07 -0500 Subject: [PATCH 4/7] better scratch 2 bevel filter shadow edges aren't clipped twice. i don't think the old filter would blur in firefox but the general method used usually would. this new method uses patterns that shouldn't have that happen. this fits in better with snap block changes in later commits. --- scratch2/style.js | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/scratch2/style.js b/scratch2/style.js index 4268a58e..85f4875c 100644 --- a/scratch2/style.js +++ b/scratch2/style.js @@ -1,6 +1,5 @@ import SVG from "./draw.js" import Color from "../shared/color.js" -import Filter from "./filter.js" import cssContent from "./style.css.js" export default class Style { @@ -1317,27 +1316,31 @@ export default class Style { } static bevelFilter(id, inset) { - const f = new Filter(id) - - const alpha = "SourceAlpha" - const s = inset ? -1 : 1 - const blur = f.blur(1, alpha) + return SVG.withChildren( + SVG.el("filter", { id, x: "0", y: "0", width: "1", height: "1" }), + [ + SVG.el("feOffset", { dx: inset ? "1" : "-1", dy: inset ? "1" : "-1", in: "SourceAlpha" }), + SVG.el("feGaussianBlur", { stdDeviation: "1", edgeMode: "none" }), + SVG.el("feColorMatrix", { type: "matrix", values: ` + 0 0 0 0 0 + 0 0 0 0 0 + 0 0 0 0 0 + 0 0 0 -.7 .7 + `, result: "shadow" }), - f.merge([ - "SourceGraphic", - f.comp( - "in", - f.flood("#fff", 0.15), - f.subtract(alpha, f.offset(+s, +s, blur)), - ), - f.comp( - "in", - f.flood("#000", 0.7), - f.subtract(alpha, f.offset(-s, -s, blur)), - ), - ]) + SVG.el("feOffset", { dx: inset ? "-1" : "1", dy: inset ? "-1" : "1", in: "SourceAlpha" }), + SVG.el("feGaussianBlur", { stdDeviation: "1", edgeMode: "none" }), + SVG.el("feColorMatrix", { type: "matrix", values: ` + 0 0 0 0 1 + 0 0 0 0 1 + 0 0 0 0 1 + 0 0 0 -.15 .15 + ` }), - return f.el + SVG.el("feComposite", { operator: "atop", in2: "SourceGraphic" }), + SVG.el("feComposite", { operator: "atop", in: "shadow" }), + ], + ) } static darkFilter(id) { From 645a7c682af72522ad999c34741b2abd4bc32b10 Mon Sep 17 00:00:00 2001 From: SArpnt Date: Wed, 5 Mar 2025 19:27:47 -0500 Subject: [PATCH 5/7] fix blurry drop shadow text in firefox, better drop shadow at normal scale previously filter bounds and particular effects (mostly feFlood) would cause some problems with element dimensions in firefox and make that element blurry and incorrectly positioned. many filters also had coordinates rounded differently between the browsers. this new svg filter act the same between browsers and is nicer and simpler in several ways --- snap/blocks.js | 1 + snap/style.js | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/snap/blocks.js b/snap/blocks.js index 131cbd68..c04f8300 100644 --- a/snap/blocks.js +++ b/snap/blocks.js @@ -2636,6 +2636,7 @@ export class DocumentView { group.querySelectorAll(".snap-drop-shadow").forEach(el => { el.style.filter = `url(#snapDropShadow-${this.id})` + el.setAttribute("transform", `${el.getAttribute("transform") || ""} translate(-1 -1)`) }) group.querySelectorAll(".snap-bevel").forEach(el => { el.style.filter = `url(#snapBevelFilter-${this.id})` diff --git a/snap/style.js b/snap/style.js index 87155c09..11e24aeb 100644 --- a/snap/style.js +++ b/snap/style.js @@ -2004,18 +2004,22 @@ export default class Style { /** * Create dropshadow filter + * make sure to also use `transform:translate(-1px,-1px);` on the element + * the movement can't be done in the filter due to issues in firefox * * @static * @param {string} id * @returns {SVGFilterElement} */ static dropShadowFilter(id) { - const f = new Filter(id) - // f.dropShadow(-0.5, -0.5, 0, "black", 0.3) - let flood = f.flood("#000", 0.3, "SourceAlpha") - let offset = f.offset(-0.6, -0.6, f.blur(0, "SourceAlpha")) - f.comp("over", "SourceGraphic", f.comp("in", flood, offset)) - return f.el + return SVG.withChildren( + SVG.el("filter", { id, x: "0", y: "0", width: "2", height: "2" }), + [ + SVG.el("feOffset", { dx: "1", dy: "1", result: "o" }), + SVG.el("feComposite", { operator: "arithmetic", k2: ".3", in: "SourceAlpha" }), + SVG.el("feComposite", { in: "o" }), + ], + ) } /** From b2dd3d24a61e261051be9dbfb6b66a96312f0d1b Mon Sep 17 00:00:00 2001 From: SArpnt Date: Wed, 5 Mar 2025 19:30:16 -0500 Subject: [PATCH 6/7] overhaul snap block bevel filter similar to the previous commit, the block bevel caused blur and misalignment in firefox. this new filter is also more accurate in general due to other tweaks. it looks slightly different between browsers but it's close enough. --- snap/style.js | 58 ++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/snap/style.js b/snap/style.js index 11e24aeb..3bc4e70d 100644 --- a/snap/style.js +++ b/snap/style.js @@ -1,6 +1,5 @@ import SVG from "./draw.js" import Color from "../shared/color.js" -import Filter from "./filter.js" import cssContent from "./style.css.js" export default class Style { @@ -2027,36 +2026,43 @@ export default class Style { * * @static * @param {string} id - * @param {number} inset + * @param {boolean} inset * @returns {SVGFilterElement} */ static bevelFilter(id, inset) { - const f = new Filter(id) - - const alpha = "SourceAlpha" - const blur = f.blur(0.3, alpha) + return SVG.withChildren( + SVG.el("filter", { id }), + [ + SVG.el("feOffset", { dx: inset ? "1" : "-1", dy: inset ? "1" : "-1", in: "SourceAlpha" }), + SVG.el("feGaussianBlur", { stdDeviation: ".6", edgeMode: "none" }), + SVG.el("feColorMatrix", { type: "matrix", values: ` + 0 0 0 0 0 + 0 0 0 0 0 + 0 0 0 0 0 + 0 0 0 -1.1 .9 + `, result: "shadow" }), - f.merge([ - "SourceGraphic", - f.comp( - "in", - f.flood("#fff", 0.4), - f.subtract( - alpha, - f.offset(inset ? -0.4 : 0.4, inset ? -0.4 : 0.4, blur), - ), - ), - f.comp( - "in", - f.flood("#000", inset ? 0.9 : 0.8), - f.subtract( - alpha, - f.offset(inset ? 0.7 : -0.7, inset ? 0.7 : -0.7, blur), - ), - ), - ]) + SVG.el("feOffset", { dx: inset ? "-1" : "1", dy: inset ? "-1" : "1", in: "SourceAlpha" }), + SVG.el("feGaussianBlur", { stdDeviation: ".6", edgeMode: "none" }), + SVG.el("feColorMatrix", { type: "matrix", values: ` + 0 0 0 0 1 + 0 0 0 0 1 + 0 0 0 0 1 + 0 0 0 -.7 .5 + `, result: "highlight" }), - return f.el + // offset .4px, .2px + SVG.el("feConvolveMatrix", { + order: "2", + divisor: "100", + kernelMatrix: inset ? "100 0 0 0" : "48 32 12 8", + edgeMode: "none", + in: "SourceGraphic" + }), + SVG.el("feComposite", { operator: "atop", in: "highlight" }), + SVG.el("feComposite", { operator: "atop", in: "shadow" }), + ], + ) } /** From 9a062e3424e66d9c5115fc51fefd297636af9e05 Mon Sep 17 00:00:00 2001 From: SArpnt Date: Wed, 5 Mar 2025 12:19:06 -0500 Subject: [PATCH 7/7] remove all copies of filter.js they contain serious bugs, make inefficient output, usually cause blur on firefox, and aren't much more ergonomic than writing out the filter manually. --- scratch2/filter.js | 78 --------------------- scratch3/filter.js | 88 ------------------------ snap/filter.js | 164 --------------------------------------------- 3 files changed, 330 deletions(-) delete mode 100644 scratch2/filter.js delete mode 100644 scratch3/filter.js delete mode 100644 snap/filter.js diff --git a/scratch2/filter.js b/scratch2/filter.js deleted file mode 100644 index dbd2934a..00000000 --- a/scratch2/filter.js +++ /dev/null @@ -1,78 +0,0 @@ -import SVG from "./draw.js" - -export default class Filter { - constructor(id, props) { - this.el = SVG.el("filter", { - ...props, - id: id, - x0: "-50%", - y0: "-50%", - width: "200%", - height: "200%", - }) - this.highestId = 0 - } - - fe(name, props, children) { - const shortName = name.toLowerCase().replace(/gaussian|osite/, "") - const id = `${shortName}-${++this.highestId}` - this.el.appendChild( - SVG.withChildren( - SVG.el(`fe${name}`, { ...props, result: id }), - children || [], - ), - ) - return id - } - - comp(op, in1, in2, props) { - return this.fe("Composite", { ...props, operator: op, in: in1, in2: in2 }) - } - - subtract(in1, in2) { - return this.comp("arithmetic", in1, in2, { k2: +1, k3: -1 }) - } - - offset(dx, dy, in1) { - return this.fe("Offset", { - in: in1, - dx: dx, - dy: dy, - }) - } - - flood(color, opacity, in1) { - return this.fe("Flood", { - in: in1, - "flood-color": color, - "flood-opacity": opacity, - }) - } - - blur(dev, in1) { - return this.fe("GaussianBlur", { - in: in1, - stdDeviation: `${dev} ${dev}`, - }) - } - - colorMatrix(in1, values) { - return this.fe("ColorMatrix", { - in: in1, - type: "matrix", - values: values.join(" "), - }) - } - - merge(children) { - this.fe( - "Merge", - {}, - children.map(name => - SVG.el("feMergeNode", { - in: name, - }), - ), - ) - } -} diff --git a/scratch3/filter.js b/scratch3/filter.js deleted file mode 100644 index c4e07294..00000000 --- a/scratch3/filter.js +++ /dev/null @@ -1,88 +0,0 @@ -import SVG from "./draw.js" - -export default class Filter { - constructor(id, props) { - this.el = SVG.el("filter", { - ...props, - id: id, - x0: "-50%", - y0: "-50%", - width: "200%", - height: "200%", - }) - this.highestId = 0 - } - - fe(name, props, children) { - const shortName = name.toLowerCase().replace(/gaussian|osite/, "") - const id = `${shortName}-${++this.highestId}` - this.el.appendChild( - SVG.withChildren( - SVG.el(`fe${name}`, { ...props, result: id }), - children || [], - ), - ) - return id - } - - comp(op, in1, in2, props) { - return this.fe("Composite", { ...props, operator: op, in: in1, in2: in2 }) - } - - subtract(in1, in2) { - return this.comp("arithmetic", in1, in2, { k2: +1, k3: -1 }) - } - - offset(dx, dy, in1) { - return this.fe("Offset", { - in: in1, - dx: dx, - dy: dy, - }) - } - - flood(color, opacity, in1) { - return this.fe("Flood", { - in: in1, - "flood-color": color, - "flood-opacity": opacity, - }) - } - - blur(dev, in1) { - return this.fe("GaussianBlur", { - in: in1, - stdDeviation: `${dev} ${dev}`, - }) - } - - dropShadow(dx, dy, blur, color, opacity) { - return this.fe("DropShadow", { - dx: dx, - dy: dy, - stdDeviation: blur, - "flood-color": color, - "flood-opacity": opacity, - }) - } - - colorMatrix(in1, values) { - return this.fe("ColorMatrix", { - in: in1, - type: "matrix", - values: values.join(" "), - }) - } - - merge(children) { - this.fe( - "Merge", - {}, - children.map(name => - SVG.el("feMergeNode", { - in: name, - }), - ), - ) - } -} diff --git a/snap/filter.js b/snap/filter.js deleted file mode 100644 index 7275c27a..00000000 --- a/snap/filter.js +++ /dev/null @@ -1,164 +0,0 @@ -import SVG from "./draw.js" - -export default class Filter { - /** - * Creates an instance of Filter. - * - * @constructor - * @param {string} id - * @param {Object} props - */ - constructor(id, props) { - this.el = SVG.el("filter", { - ...props, - id: id, - x0: "-50%", - y0: "-50%", - width: "200%", - height: "200%", - }) - this.highestId = 0 - } - - /** - * Filter element - * - * @param {string} name - * @param {Object} props - * @param {SVGElement[]} children - * @returns {string} - Filter id - */ - fe(name, props, children) { - const shortName = name.toLowerCase().replace(/gaussian|osite/, "") - const id = `${shortName}-${++this.highestId}` - this.el.appendChild( - SVG.withChildren( - SVG.el(`fe${name}`, { ...props, result: id }), - children || [], - ), - ) - return id - } - - /** - * Add composite filter - * - * @param {string} op - * @param {string} in1 - * @param {string} in2 - * @param {Object} props - * @returns {string} - */ - comp(op, in1, in2, props) { - return this.fe("Composite", { ...props, operator: op, in: in1, in2: in2 }) - } - - /** - * Subtract filter - * - * @param {string} in1 - * @param {string} in2 - * @returns {string} - */ - subtract(in1, in2) { - return this.comp("arithmetic", in1, in2, { k2: +1, k3: -1 }) - } - - /** - * Offset filter - * - * @param {number} dx - * @param {number} dy - * @param {string} in1 - * @returns {string} - */ - offset(dx, dy, in1) { - return this.fe("Offset", { - in: in1, - dx: dx, - dy: dy, - }) - } - - /** - * Flood filter - * - * @param {string} color - * @param {number} opacity - * @param {string} in1 - * @returns {string} - */ - flood(color, opacity, in1) { - return this.fe("Flood", { - in: in1, - "flood-color": color, - "flood-opacity": opacity, - }) - } - - /** - * Blur filter - * - * @param {number} dev - * @param {string} in1 - * @returns {string} - */ - blur(dev, in1) { - return this.fe("GaussianBlur", { - in: in1, - stdDeviation: `${dev} ${dev}`, - }) - } - - /** - * Dropshadow filter - * - * @param {number} dx - * @param {number} dy - * @param {number} blur - * @param {string} color - * @param {number} opacity - * @returns {string} - */ - dropShadow(dx, dy, blur, color, opacity) { - return this.fe("DropShadow", { - dx: dx, - dy: dy, - stdDeviation: blur, - "flood-color": color, - "flood-opacity": opacity, - }) - } - - /** - * Color matrix - * - * @param {string} in1 - * @param {string[]} values - * @returns {string} - */ - colorMatrix(in1, values) { - return this.fe("ColorMatrix", { - in: in1, - type: "matrix", - values: values.join(" "), - }) - } - - /** - * Merge - * - * @param {string[]} children - Filter ids - */ - merge(children) { - this.fe( - "Merge", - {}, - children.map(name => - SVG.el("feMergeNode", { - in: name, - }), - ), - ) - } -}