windowseat-reflection-removal-web / js /comparison-widget.js
Daniyar Zakarin
Bug fix: dissapearing comparison slider.
1bc1fc6
// Comparison widget logic
// Map model -> image path
const WS_MODEL_IMAGES = {
dai: '/images/comparison/car/dai.png',
dsit: '/images/comparison/car/dsit.png',
dsrnet: '/images/comparison/car/dsrnet.png',
rdnet: '/images/comparison/car/rdnet.png',
};
// Example hotspot configuration (percent coordinates)
const WS_HOTSPOTS = [
{ left: '25%', top: '60%' },
{ left: '60%', top: '40%' },
{ left: '75%', top: '70%' },
];
function wsRenderHotspots() {
const $layer = $('#ws-hotspots-layer');
if (!$layer.length) return;
$layer.empty();
WS_HOTSPOTS.forEach((pos) => {
const $hotspot = $('<div/>')
.addClass('ws-hotspot')
.css({
left: pos.left,
top: pos.top,
transform: 'translate(-50%, -50%)',
});
const $arrow = $('<div/>').addClass('ws-hotspot-arrow');
$hotspot.append($arrow);
$layer.append($hotspot);
});
}
// Scene-based image mapping
const WS_SCENE_IMAGES = {
car: {
original: '/images/comparison/car/original.jpg',
windowseat: '/images/comparison/car/windowseat.png',
dai: '/images/comparison/car/dai.png',
dsit: '/images/comparison/car/dsit.png',
dsrnet: '/images/comparison/car/dsrnet.png',
rdnet: '/images/comparison/car/rdnet.png',
},
uniqlo: {
original: '/images/comparison/uniqlo/original.jpg',
windowseat: '/images/comparison/uniqlo/windowseat.png',
dai: '/images/comparison/uniqlo/dai.jpg',
dsit: '/images/comparison/uniqlo/dsit.png',
dsrnet: '/images/comparison/uniqlo/dsrnet.png',
rdnet: '/images/comparison/uniqlo/rdnet.png',
},
entrance: {
original: '/images/comparison/entrance/original.jpg',
windowseat: '/images/comparison/entrance/windowseat.png',
dai: '/images/comparison/entrance/dai.jpg',
dsit: '/images/comparison/entrance/dsit.png',
dsrnet: '/images/comparison/entrance/dsrnet.png',
rdnet: '/images/comparison/entrance/rdnet.png',
},
zoo: {
original: '/images/comparison/zoo/original.jpg',
windowseat: '/images/comparison/zoo/windowseat.png',
dai: '/images/comparison/zoo/dai.jpg',
dsit: '/images/comparison/zoo/dsit.png',
dsrnet: '/images/comparison/zoo/dsrnet.png',
rdnet: '/images/comparison/zoo/rdnet.png',
},
bakery: {
original: '/images/comparison/bakery/original.jpg',
windowseat: '/images/comparison/bakery/windowseat.png',
dai: '/images/comparison/bakery/dai.jpg',
dsit: '/images/comparison/bakery/dsit.png',
dsrnet: '/images/comparison/bakery/dsrnet.png',
rdnet: '/images/comparison/bakery/rdnet.png',
},
museum: {
original: '/images/comparison/museum/original.jpg',
windowseat: '/images/comparison/museum/windowseat.png',
dai: '/images/comparison/museum/dai.jpg',
dsit: '/images/comparison/museum/dsit.png',
dsrnet: '/images/comparison/museum/dsrnet.png',
rdnet: '/images/comparison/museum/rdnet.png',
},
};
function wsLoadScene(sceneName) {
const sceneData = WS_SCENE_IMAGES[sceneName];
if (!sceneData) return;
// Update left image (original input)
$('#ws-image-left').attr('src', sceneData.original);
// Update right image with current selected model
const activeModel = $('.ws-model-pill[data-model].is-active').data('model') || 'dai';
$('#ws-image-right').attr('src', sceneData[activeModel]);
}
$(document).ready(function() {
// Initialize state
let currentScene = 'car';
let currentModel = 'dai';
let sliderInitialized = false;
// map model keys -> readable label shown on the slider
const WS_MODEL_LABELS = {
dai: 'DAI',
dsit: 'DSIT',
dsrnet: 'DSRNet',
rdnet: 'RDNet',
};
// preload helper
function wsPreloadImage(url) {
return new Promise((resolve, reject) => {
if (!url) return resolve();
// if already loaded by browser, resolve quickly
const cached = Array.from(document.images).find(img => img.src && img.src.endsWith(url));
const img = new Image();
img.onload = () => resolve(url);
img.onerror = () => reject(new Error('Failed to load ' + url));
img.src = url;
});
}
// Initialize the twentytwenty slider
function initSlider(beforeLabel = 'Baseline') {
$("#ws-comparison-slider").twentytwenty({
before_label: beforeLabel,
after_label: 'WindowSeat',
default_offset_pct: 0.5,
});
sliderInitialized = true;
// Clear any accidental zero clip applied by the plugin
setTimeout(() => {
$('.twentytwenty-overlay').css('clip', 'auto');
}, 50);
}
// Update labels on an already-initialized twentytwenty instance
function wsSetSliderLabels(beforeLabel, afterLabel) {
// plugin creates either "-label" or plain classes in different versions; update both
$('#ws-comparison-slider .twentytwenty-before-label').attr('data-content', afterLabel);
$('#ws-comparison-slider .twentytwenty-after-label').attr('data-content', beforeLabel);
const $slider = $('#ws-comparison-slider');
const $api = $slider.data('twentytwenty');
if ($api) {
// log something
$api.adjustSlider(0.5); // reset to center
}
// log that labels were updated
console.log('Updated slider labels to:', $wsSlider.find('.twentytwenty-before-label').attr('data-content'),
$wsSlider.find('.twentytwenty-after-label').attr('data-content'));
// log if $wsSlider exists
console.log('Slider element exists:', $wsSlider.length > 0);
}
// Function to update images
function updateImages(scene, model) {
currentScene = scene;
currentModel = model;
const sceneData = WS_SCENE_IMAGES[scene];
if (!sceneData) return;
// Preload all images we will use, then swap and (re)initialize slider.
const leftUrl = sceneData.original;
const windowseatUrl = sceneData.windowseat;
const baselineUrl = sceneData[model];
// show light loading state if you have CSS for it
$('#ws-comparison-container').addClass('ws-loading');
Promise.all([
wsPreloadImage(leftUrl),
wsPreloadImage(windowseatUrl),
wsPreloadImage(baselineUrl),
]).then(() => {
// swap sources only after loaded
$('#ws-image-left').attr('src', leftUrl);
$('#ws-image-windowseat').attr('src', windowseatUrl);
$('#ws-image-baseline').attr('src', baselineUrl);
const modelLabel = WS_MODEL_LABELS[model] || model;
// log sliderInitialized
// destroy only if already initialized
// if (sliderInitialized) {
// $('#ws-comparison-slider').trigger('destroy');
// sliderInitialized = false;
// }
// small delay to allow browser layout, then init
setTimeout(() => {
// initSlider(modelLabel);
// if the plugin was already initialized and you prefer updating labels instead of reinit:
wsSetSliderLabels('WindowSeat', modelLabel);
$('#ws-comparison-container').removeClass('ws-loading');
}, 60);
}).catch((err) => {
console.warn('Image preload failed:', err);
// fallback: swap anyway and try init
$('#ws-image-left').attr('src', leftUrl);
$('#ws-image-windowseat').attr('src', windowseatUrl);
$('#ws-image-baseline').attr('src', baselineUrl);
if (sliderInitialized) $('#ws-comparison-slider').trigger('destroy');
// setTimeout(initSlider, 100);
wsSetSliderLabels('WindowSeat', modelLabel);
$('#ws-comparison-container').removeClass('ws-loading');
});
}
// Gallery item clicks
$('.ws-gallery-item').on('click', function() {
$('.ws-gallery-item').removeClass('is-active');
$(this).addClass('is-active');
const scene = $(this).data('scene');
updateImages(scene, currentModel);
});
// Model selector clicks
$('.ws-model-pill[data-model]').on('click', function() {
$('.ws-model-pill[data-model]').removeClass('is-active');
$(this).addClass('is-active');
const model = $(this).data('model');
updateImages(currentScene, model);
});
// Initialize with default scene and model
updateImages(currentScene, currentModel);
// Remove or comment out hotspot code:
/*
// Hotspot toggle
let hotspotsVisible = false;
$('#ws-hotspots-toggle').on('click', function() {
hotspotsVisible = !hotspotsVisible;
if (hotspotsVisible) {
wsRenderHotspots();
} else {
$('#ws-hotspots-layer').empty();
}
});
*/
});