mirror of
https://github.com/konvajs/konva.git
synced 2025-09-19 10:47:59 +08:00
754 lines
24 KiB
HTML
754 lines
24 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>KonvaJS Sandbox</title>
|
|
<meta
|
|
name="viewport"
|
|
content="width=device-width, initial-scale=1.0, user-scalable=1.0, minimum-scale=1.0, maximum-scale=1.0"
|
|
/>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
padding: 20px;
|
|
font-family: Arial, sans-serif;
|
|
background: #f0f0f0;
|
|
}
|
|
|
|
.main-container {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.section {
|
|
background: white;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.demo-container {
|
|
display: flex;
|
|
gap: 20px;
|
|
min-height: 400px;
|
|
}
|
|
|
|
.demo-section {
|
|
flex: 1;
|
|
position: relative;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.default-filters {
|
|
border-top: 3px solid #e74c3c;
|
|
}
|
|
.custom-filters {
|
|
border-top: 3px solid #27ae60;
|
|
}
|
|
|
|
.controls {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
top: 10px;
|
|
left: 10px;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
border: 2px solid #ddd;
|
|
min-width: 250px;
|
|
max-height: 350px;
|
|
overflow-y: auto;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
z-index: 100;
|
|
}
|
|
|
|
.control-group {
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.control-group:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
font-size: 12px;
|
|
}
|
|
|
|
input[type='range'] {
|
|
width: 100%;
|
|
margin-bottom: 3px;
|
|
}
|
|
|
|
.value-display {
|
|
color: #666;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.filter-buttons {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
button {
|
|
flex: 1;
|
|
padding: 8px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-weight: bold;
|
|
font-size: 11px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
button.active {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.emboss-btn {
|
|
background: #3498db;
|
|
color: white;
|
|
}
|
|
.emboss-btn.active {
|
|
background: #2980b9;
|
|
}
|
|
|
|
.solarize-btn {
|
|
background: #f39c12;
|
|
color: white;
|
|
}
|
|
.solarize-btn.active {
|
|
background: #e67e22;
|
|
}
|
|
|
|
select {
|
|
width: 100%;
|
|
padding: 3px;
|
|
border-radius: 3px;
|
|
border: 1px solid #ddd;
|
|
font-size: 11px;
|
|
}
|
|
|
|
input[type='checkbox'] {
|
|
margin-right: 5px;
|
|
}
|
|
</style>
|
|
<!-- <script src="https://cdn.rawgit.com/hammerjs/touchemulator/master/touch-emulator.js"></script> -->
|
|
<!-- <script src="https://unpkg.com/gifler@0.1.0/gifler.min.js"></script> -->
|
|
<script>
|
|
// TouchEmulator();
|
|
</script>
|
|
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.7/hammer.js"></script> -->
|
|
<!-- <script src="https://cdn.rawgit.com/hammerjs/touchemulator/master/touch-emulator.js"></script> -->
|
|
<!-- <script src="./hammer.js"></script> -->
|
|
<!-- <script src="https://unpkg.com/fabric@5.2.1/dist/fabric.js"></script> -->
|
|
</head>
|
|
|
|
<body>
|
|
<div class="main-container">
|
|
<h1 style="text-align: center; margin-bottom: 30px; color: #333">
|
|
Filter Comparison: Default vs Custom
|
|
</h1>
|
|
|
|
<div class="section">
|
|
<div class="demo-container">
|
|
<!-- Default Filters -->
|
|
<div class="demo-section default-filters">
|
|
<div
|
|
id="default-container"
|
|
style="width: 100%; height: 400px"
|
|
></div>
|
|
<div class="controls">
|
|
<h3 style="margin: 0 0 10px 0; color: #e74c3c; font-size: 14px">
|
|
Default Konva Filters
|
|
</h3>
|
|
|
|
<div class="filter-buttons">
|
|
<button id="emboss-default" class="emboss-btn active">
|
|
Emboss
|
|
</button>
|
|
<button id="solarize-default" class="solarize-btn">
|
|
Solarize
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Default Emboss Controls -->
|
|
<div id="emboss-controls-default">
|
|
<div class="control-group">
|
|
<label for="emboss-strength-default"
|
|
>Strength:
|
|
<span
|
|
id="emboss-strength-value-default"
|
|
class="value-display"
|
|
>0.5</span
|
|
></label
|
|
>
|
|
<input
|
|
type="range"
|
|
id="emboss-strength-default"
|
|
min="0"
|
|
max="1"
|
|
value="0.5"
|
|
step="0.1"
|
|
/>
|
|
</div>
|
|
<div class="control-group">
|
|
<label for="emboss-white-default"
|
|
>White Level:
|
|
<span id="emboss-white-value-default" class="value-display"
|
|
>0.5</span
|
|
></label
|
|
>
|
|
<input
|
|
type="range"
|
|
id="emboss-white-default"
|
|
min="0"
|
|
max="1"
|
|
value="0.5"
|
|
step="0.1"
|
|
/>
|
|
</div>
|
|
<div class="control-group">
|
|
<label for="emboss-direction-default">Direction:</label>
|
|
<select id="emboss-direction-default">
|
|
<option value="top-left" selected>Top Left</option>
|
|
<option value="top">Top</option>
|
|
<option value="top-right">Top Right</option>
|
|
<option value="right">Right</option>
|
|
<option value="bottom-right">Bottom Right</option>
|
|
<option value="bottom">Bottom</option>
|
|
<option value="bottom-left">Bottom Left</option>
|
|
<option value="left">Left</option>
|
|
</select>
|
|
</div>
|
|
<div class="control-group">
|
|
<label
|
|
><input type="checkbox" id="emboss-blend-default" /> Blend
|
|
Mode</label
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Default Solarize Controls -->
|
|
<div id="solarize-controls-default" style="display: none">
|
|
<div class="control-group">
|
|
<label for="solarize-threshold-default"
|
|
>Threshold:
|
|
<span
|
|
id="solarize-threshold-value-default"
|
|
class="value-display"
|
|
>127</span
|
|
></label
|
|
>
|
|
<input
|
|
type="range"
|
|
id="solarize-threshold-default"
|
|
min="0"
|
|
max="255"
|
|
value="127"
|
|
step="1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Custom Filters -->
|
|
<div class="demo-section custom-filters">
|
|
<div id="custom-container" style="width: 100%; height: 400px"></div>
|
|
<div class="controls">
|
|
<h3 style="margin: 0 0 10px 0; color: #27ae60; font-size: 14px">
|
|
Custom Implementations
|
|
</h3>
|
|
|
|
<div class="filter-buttons">
|
|
<button id="emboss-custom" class="emboss-btn active">
|
|
Emboss
|
|
</button>
|
|
<button id="solarize-custom" class="solarize-btn">
|
|
Solarize
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Custom Emboss Controls -->
|
|
<div id="emboss-controls-custom">
|
|
<div class="control-group">
|
|
<label for="emboss-strength-custom"
|
|
>Strength:
|
|
<span
|
|
id="emboss-strength-value-custom"
|
|
class="value-display"
|
|
>1</span
|
|
></label
|
|
>
|
|
<input
|
|
type="range"
|
|
id="emboss-strength-custom"
|
|
min="0"
|
|
max="3"
|
|
value="1"
|
|
step="0.1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Custom Solarize Controls -->
|
|
<div id="solarize-controls-custom" style="display: none">
|
|
<div class="control-group">
|
|
<label for="solarize-threshold-custom"
|
|
>Threshold:
|
|
<span
|
|
id="solarize-threshold-value-custom"
|
|
class="value-display"
|
|
>128</span
|
|
></label
|
|
>
|
|
<input
|
|
type="range"
|
|
id="solarize-threshold-custom"
|
|
min="0"
|
|
max="255"
|
|
value="128"
|
|
step="1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="module">
|
|
import Konva from '../src/index.ts';
|
|
|
|
// ===============================
|
|
// Custom filter implementations
|
|
// ===============================
|
|
function solarizeImageData(imageData, threshold = 128) {
|
|
const d = imageData.data;
|
|
for (let i = 0; i < d.length; i += 4) {
|
|
const r = d[i],
|
|
g = d[i + 1],
|
|
b = d[i + 2];
|
|
// sRGB luma
|
|
const L = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
if (L >= threshold) {
|
|
d[i] = 255 - r;
|
|
d[i + 1] = 255 - g;
|
|
d[i + 2] = 255 - b;
|
|
}
|
|
}
|
|
return imageData;
|
|
}
|
|
|
|
function embossImageData(imageData) {
|
|
const data = imageData.data;
|
|
const w = imageData.width;
|
|
const h = imageData.height;
|
|
|
|
debugger;
|
|
// Inputs from Konva node
|
|
const strength01 = Math.min(
|
|
1,
|
|
Math.max(0, this.embossStrength?.() ?? 0.5)
|
|
); // [0..1]
|
|
const whiteLevel01 = Math.min(
|
|
1,
|
|
Math.max(0, this.embossWhiteLevel?.() ?? 0.5)
|
|
); // [0..1]
|
|
// Convert string direction to degrees
|
|
const directionMap = {
|
|
'top-left': 315,
|
|
top: 270,
|
|
'top-right': 225,
|
|
right: 180,
|
|
'bottom-right': 135,
|
|
bottom: 90,
|
|
'bottom-left': 45,
|
|
left: 0,
|
|
};
|
|
const directionDeg =
|
|
directionMap[this.embossDirection?.() ?? 'top-left'] ?? 315; // degrees
|
|
const blend = !!(this.embossBlend?.() ?? false);
|
|
|
|
// Internal mapping:
|
|
// - Pixastic "strength" was 0..10; we honor your 0..1 API and scale accordingly.
|
|
// - Sobel directional response is roughly in [-1020..1020] for 8-bit luminance; scale to ~±128.
|
|
const strength = strength01 * 10;
|
|
const bias = whiteLevel01 * 255;
|
|
const dirRad = (directionDeg * Math.PI) / 180;
|
|
const cx = Math.cos(dirRad);
|
|
const cy = Math.sin(dirRad);
|
|
const SCALE = (128 / 1020) * strength; // ≈0.1255 * strength
|
|
|
|
// Precompute luminance (Rec.709)
|
|
const src = new Uint8ClampedArray(data); // snapshot
|
|
const lum = new Float32Array(w * h);
|
|
for (let p = 0, i = 0; i < data.length; i += 4, p++) {
|
|
lum[p] = 0.2126 * src[i] + 0.7152 * src[i + 1] + 0.0722 * src[i + 2];
|
|
}
|
|
|
|
// Sobel kernels (flattened)
|
|
const Gx = [-1, 0, 1, -2, 0, 2, -1, 0, 1];
|
|
const Gy = [-1, -2, -1, 0, 0, 0, 1, 2, 1];
|
|
|
|
// neighbor offsets around center pixel in lum space
|
|
const OFF = [-w - 1, -w, -w + 1, -1, 0, 1, w - 1, w, w + 1];
|
|
|
|
// Helpers
|
|
const clamp8 = (v) => (v < 0 ? 0 : v > 255 ? 255 : v);
|
|
|
|
// Process: leave a 1px border unchanged (faster/cleaner)
|
|
for (let y = 1; y < h - 1; y++) {
|
|
for (let x = 1; x < w - 1; x++) {
|
|
const p = y * w + x;
|
|
|
|
// Directional derivative = (cosθ * Gx + sinθ * Gy) • neighborhood(lum)
|
|
let sx = 0,
|
|
sy = 0;
|
|
// unroll loop for speed
|
|
sx += lum[p + OFF[0]] * Gx[0];
|
|
sy += lum[p + OFF[0]] * Gy[0];
|
|
sx += lum[p + OFF[1]] * Gx[1];
|
|
sy += lum[p + OFF[1]] * Gy[1];
|
|
sx += lum[p + OFF[2]] * Gx[2];
|
|
sy += lum[p + OFF[2]] * Gy[2];
|
|
sx += lum[p + OFF[3]] * Gx[3];
|
|
sy += lum[p + OFF[3]] * Gy[3];
|
|
// center has 0 weights in both Sobel masks; can skip if desired
|
|
sx += lum[p + OFF[5]] * Gx[5];
|
|
sy += lum[p + OFF[5]] * Gy[5];
|
|
sx += lum[p + OFF[6]] * Gx[6];
|
|
sy += lum[p + OFF[6]] * Gy[6];
|
|
sx += lum[p + OFF[7]] * Gx[7];
|
|
sy += lum[p + OFF[7]] * Gy[7];
|
|
sx += lum[p + OFF[8]] * Gx[8];
|
|
sy += lum[p + OFF[8]] * Gy[8];
|
|
|
|
const r = cx * sx + cy * sy; // directional response
|
|
const outGray = clamp8(bias + r * SCALE); // biased, scaled, clamped
|
|
|
|
const o = p * 4;
|
|
if (blend) {
|
|
// Add the emboss "relief" around chosen bias to original RGB
|
|
const delta = outGray - bias; // symmetric around whiteLevel
|
|
data[o] = clamp8(src[o] + delta);
|
|
data[o + 1] = clamp8(src[o + 1] + delta);
|
|
data[o + 2] = clamp8(src[o + 2] + delta);
|
|
data[o + 3] = src[o + 3];
|
|
} else {
|
|
// Grayscale embossed output
|
|
data[o] = data[o + 1] = data[o + 2] = outGray;
|
|
data[o + 3] = src[o + 3];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy border (untouched) to keep edges clean
|
|
// top & bottom rows
|
|
for (let x = 0; x < w; x++) {
|
|
let oTop = x * 4,
|
|
oBot = ((h - 1) * w + x) * 4;
|
|
data[oTop] = src[oTop];
|
|
data[oTop + 1] = src[oTop + 1];
|
|
data[oTop + 2] = src[oTop + 2];
|
|
data[oTop + 3] = src[oTop + 3];
|
|
data[oBot] = src[oBot];
|
|
data[oBot + 1] = src[oBot + 1];
|
|
data[oBot + 2] = src[oBot + 2];
|
|
data[oBot + 3] = src[oBot + 3];
|
|
}
|
|
// left & right columns
|
|
for (let y = 1; y < h - 1; y++) {
|
|
let oL = y * w * 4,
|
|
oR = (y * w + (w - 1)) * 4;
|
|
data[oL] = src[oL];
|
|
data[oL + 1] = src[oL + 1];
|
|
data[oL + 2] = src[oL + 2];
|
|
data[oL + 3] = src[oL + 3];
|
|
data[oR] = src[oR];
|
|
data[oR + 1] = src[oR + 1];
|
|
data[oR + 2] = src[oR + 2];
|
|
data[oR + 3] = src[oR + 3];
|
|
}
|
|
|
|
return imageData;
|
|
}
|
|
|
|
// Add custom properties
|
|
// Konva.Factory.addGetterSetter(Konva.Node, 'solarizeThreshold', 128);
|
|
// Konva.Factory.addGetterSetter(Konva.Node, 'embossStrengthCustom', 1);
|
|
|
|
const containerWidth = 400;
|
|
const containerHeight = 400;
|
|
|
|
// ===============================
|
|
// Default Filters Stage
|
|
// ===============================
|
|
const defaultStage = new Konva.Stage({
|
|
container: 'default-container',
|
|
width: containerWidth,
|
|
height: containerHeight,
|
|
});
|
|
|
|
const defaultLayer = new Konva.Layer();
|
|
defaultStage.add(defaultLayer);
|
|
|
|
// ===============================
|
|
// Custom Filters Stage
|
|
// ===============================
|
|
const customStage = new Konva.Stage({
|
|
container: 'custom-container',
|
|
width: containerWidth,
|
|
height: containerHeight,
|
|
});
|
|
|
|
const customLayer = new Konva.Layer();
|
|
customStage.add(customLayer);
|
|
|
|
// Load image and create both versions
|
|
const imageObj = new Image();
|
|
imageObj.onload = function () {
|
|
// Default filters image
|
|
const defaultImage = new Konva.Image({
|
|
image: imageObj,
|
|
x: 50,
|
|
y: 50,
|
|
width: 300,
|
|
height: 225,
|
|
draggable: true,
|
|
});
|
|
defaultImage.cache({ pixelRatio: 2 });
|
|
defaultImage.filters([Konva.Filters.Emboss]);
|
|
defaultLayer.add(defaultImage);
|
|
defaultLayer.draw();
|
|
|
|
// Custom filters image
|
|
const customImage = new Konva.Image({
|
|
image: imageObj,
|
|
x: 50,
|
|
y: 50,
|
|
width: 300,
|
|
height: 225,
|
|
draggable: true,
|
|
});
|
|
customImage.cache({ pixelRatio: 2 });
|
|
customImage.embossStrength(
|
|
parseFloat(document.getElementById('emboss-strength-default').value)
|
|
);
|
|
customImage.embossWhiteLevel(
|
|
parseFloat(document.getElementById('emboss-white-default').value)
|
|
);
|
|
customImage.embossDirection(
|
|
document.getElementById('emboss-direction-default').value
|
|
);
|
|
customImage.embossBlend(
|
|
document.getElementById('emboss-blend-default').checked
|
|
);
|
|
customImage.filters([embossImageData]);
|
|
customLayer.add(customImage);
|
|
customLayer.draw();
|
|
|
|
// ===============================
|
|
// Control Logic
|
|
// ===============================
|
|
|
|
let currentDefaultFilter = 'emboss';
|
|
let currentCustomFilter = 'emboss';
|
|
|
|
function updateDefaultFilter() {
|
|
if (currentDefaultFilter === 'emboss') {
|
|
defaultImage.filters([Konva.Filters.Emboss]);
|
|
defaultImage.embossStrength(
|
|
parseFloat(
|
|
document.getElementById('emboss-strength-default').value
|
|
)
|
|
);
|
|
defaultImage.embossWhiteLevel(
|
|
parseFloat(document.getElementById('emboss-white-default').value)
|
|
);
|
|
defaultImage.embossDirection(
|
|
document.getElementById('emboss-direction-default').value
|
|
);
|
|
defaultImage.embossBlend(
|
|
document.getElementById('emboss-blend-default').checked
|
|
);
|
|
} else {
|
|
// Use custom solarize function for default filter to add threshold control
|
|
defaultImage.filters([Konva.Filters.Solarize]);
|
|
}
|
|
defaultLayer.batchDraw();
|
|
updateCustomFilter();
|
|
}
|
|
|
|
function updateCustomFilter() {
|
|
console.log(currentCustomFilter);
|
|
if (currentCustomFilter === 'emboss') {
|
|
customImage.embossStrength(
|
|
parseFloat(
|
|
document.getElementById('emboss-strength-default').value
|
|
)
|
|
);
|
|
customImage.embossWhiteLevel(
|
|
parseFloat(document.getElementById('emboss-white-default').value)
|
|
);
|
|
customImage.embossDirection(
|
|
document.getElementById('emboss-direction-default').value
|
|
);
|
|
customImage.embossBlend(
|
|
document.getElementById('emboss-blend-default').checked
|
|
);
|
|
|
|
customImage.filters([embossImageData]);
|
|
} else {
|
|
customImage.filters([
|
|
(imageData) =>
|
|
solarizeImageData(
|
|
imageData,
|
|
parseFloat(
|
|
document.getElementById('solarize-threshold-custom').value
|
|
)
|
|
),
|
|
]);
|
|
}
|
|
customLayer.batchDraw();
|
|
}
|
|
|
|
// Default filter button controls
|
|
document
|
|
.getElementById('emboss-default')
|
|
.addEventListener('click', () => {
|
|
currentDefaultFilter = 'emboss';
|
|
document.getElementById('emboss-default').classList.add('active');
|
|
document
|
|
.getElementById('solarize-default')
|
|
.classList.remove('active');
|
|
document.getElementById('emboss-controls-default').style.display =
|
|
'block';
|
|
document.getElementById('solarize-controls-default').style.display =
|
|
'none';
|
|
updateDefaultFilter();
|
|
updateCustomFilter();
|
|
});
|
|
|
|
document
|
|
.getElementById('solarize-default')
|
|
.addEventListener('click', () => {
|
|
currentDefaultFilter = 'solarize';
|
|
document.getElementById('solarize-default').classList.add('active');
|
|
document
|
|
.getElementById('emboss-default')
|
|
.classList.remove('active');
|
|
document.getElementById('solarize-controls-default').style.display =
|
|
'block';
|
|
document.getElementById('emboss-controls-default').style.display =
|
|
'none';
|
|
updateDefaultFilter();
|
|
updateCustomFilter();
|
|
});
|
|
|
|
// Custom filter button controls
|
|
document
|
|
.getElementById('emboss-custom')
|
|
.addEventListener('click', () => {
|
|
currentCustomFilter = 'emboss';
|
|
document.getElementById('emboss-custom').classList.add('active');
|
|
document
|
|
.getElementById('solarize-custom')
|
|
.classList.remove('active');
|
|
document.getElementById('emboss-controls-custom').style.display =
|
|
'block';
|
|
document.getElementById('solarize-controls-custom').style.display =
|
|
'none';
|
|
updateCustomFilter();
|
|
});
|
|
|
|
document
|
|
.getElementById('solarize-custom')
|
|
.addEventListener('click', () => {
|
|
currentCustomFilter = 'solarize';
|
|
document.getElementById('solarize-custom').classList.add('active');
|
|
document.getElementById('emboss-custom').classList.remove('active');
|
|
document.getElementById('solarize-controls-custom').style.display =
|
|
'block';
|
|
document.getElementById('emboss-controls-custom').style.display =
|
|
'none';
|
|
updateCustomFilter();
|
|
});
|
|
|
|
// Default emboss controls
|
|
document
|
|
.getElementById('emboss-strength-default')
|
|
.addEventListener('input', (e) => {
|
|
document.getElementById(
|
|
'emboss-strength-value-default'
|
|
).textContent = e.target.value;
|
|
updateDefaultFilter();
|
|
updateCustomFilter;
|
|
});
|
|
|
|
document
|
|
.getElementById('emboss-white-default')
|
|
.addEventListener('input', (e) => {
|
|
document.getElementById('emboss-white-value-default').textContent =
|
|
e.target.value;
|
|
updateDefaultFilter();
|
|
updateCustomFilter;
|
|
});
|
|
|
|
document
|
|
.getElementById('emboss-direction-default')
|
|
.addEventListener('change', () => {
|
|
updateDefaultFilter();
|
|
updateCustomFilter();
|
|
});
|
|
document
|
|
.getElementById('emboss-blend-default')
|
|
.addEventListener('change', () => {
|
|
updateDefaultFilter();
|
|
updateCustomFilter();
|
|
});
|
|
|
|
// Default solarize controls
|
|
document
|
|
.getElementById('solarize-threshold-default')
|
|
.addEventListener('input', (e) => {
|
|
document.getElementById(
|
|
'solarize-threshold-value-default'
|
|
).textContent = e.target.value;
|
|
updateDefaultFilter();
|
|
updateCustomFilter();
|
|
});
|
|
|
|
// Custom filter controls
|
|
document
|
|
.getElementById('emboss-strength-custom')
|
|
.addEventListener('input', (e) => {
|
|
document.getElementById(
|
|
'emboss-strength-value-custom'
|
|
).textContent = e.target.value;
|
|
updateCustomFilter();
|
|
});
|
|
|
|
document
|
|
.getElementById('solarize-threshold-custom')
|
|
.addEventListener('input', (e) => {
|
|
document.getElementById(
|
|
'solarize-threshold-value-custom'
|
|
).textContent = e.target.value;
|
|
updateCustomFilter();
|
|
});
|
|
};
|
|
|
|
imageObj.crossOrigin = 'anonymous';
|
|
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
|
|
</script>
|
|
</body>
|
|
</html>
|