Ce module est conçu pour capturer les aperçus visuels des personnalisations réalisées par les utilisateurs dans un configurateur de produit, enregistre ces aperçus dans le localStorage, et les affiche dans le panier. Cette approche se concentre uniquement sur le frontend, ce qui simplifie son intégration.
Fonctionnalités principales
Capture du Canvas
Combinaison des calques upper-canvas et lower-canvas en une image unique.
Génération d'une image en Base64 pour l'afficher facilement.
Enregistrement dans le localStorage
Utilisation d'une clé unique basée sur l'ID produit et un timestamp.
Ajout de métadonnées (texte, police, taille du texte, etc.) si disponibles.
Affichage dans le Panier
Récupération des aperçus à partir du localStorage.
Affichage de l'image et des détails (texte, police, etc.) sous chaque article.
Suppression des données
Suppression des images et métadonnées du localStorage lorsque l'utilisateur retire un article du panier.
Structure et fonctionnement du code
1. Capture et enregistrement
Code source
document.addEventListener('DOMContentLoaded', function() {
var saveButton = document.getElementById('cv-saveCompositionButton');
if (saveButton) {
saveButton.addEventListener('click', function() {
var upperCanvas = document.querySelector('canvas.upper-canvas');
var lowerCanvas = document.querySelector('canvas.lower-canvas');
var textContentElement = document.getElementById('text-content-line-0');
var textContent = textContentElement ? textContentElement.value : null;
// Capture des autres informations : police, hauteur de texte
var fontFamilyElement = document.querySelector('#font-family-line-0-button .ui-selectmenu-text');
var fontFamily = fontFamilyElement ? fontFamilyElement.textContent : null;
var textHeightElement = document.getElementById('textHeightInput-line-0');
var textHeight = textHeightElement ? textHeightElement.value : null;
// Vérification des canvases et création d'une image combinée
if (upperCanvas && lowerCanvas) {
var combinedCanvas = document.createElement('canvas');
combinedCanvas.width = upperCanvas.width;
combinedCanvas.height = upperCanvas.height;
var ctx = combinedCanvas.getContext('2d');
ctx.drawImage(lowerCanvas, 0, 0);
ctx.drawImage(upperCanvas, 0, 0);
setTimeout(function() {
try {
var canvasData = combinedCanvas.toDataURL('image/png');
var uniqueKey = 'canvasImage_' + Date.now();
var dataToStore = { canvas: canvasData, textContent, fontFamily, textHeight };
localStorage.setItem(uniqueKey, JSON.stringify(dataToStore));
} catch (e) {
console.error('Erreur lors de la capture du canvas :', e);
}
}, 500);
}
});
}
});
Explications
Capture des données clés : contenu texte, police d'écriture, taille.
Fusion des calques pour générer une image unique.
Stockage des données dans localStorage avec une clé unique.
2. Affichage dans le panier
Code source
document.addEventListener('DOMContentLoaded', function() {
var cartItems = document.querySelectorAll('.cart-grid ul li');
cartItems.forEach(function(cartItem) {
var designerRefLabel = cartItem.querySelector('.label:contains("designer-ref:")');
if (designerRefLabel) {
var productId = designerRefLabel.innerText.match(/-(\d+)$/)[1];
var canvasKeys = Object.keys(localStorage).filter(key => key.includes(productId));
if (canvasKeys.length > 0) {
var storedData = JSON.parse(localStorage.getItem(canvasKeys[0]));
var img = document.createElement('img');
img.src = storedData.canvas;
img.style.width = '100%';
cartItem.querySelector('.product-line-grid-right').appendChild(img);
}
}
});
});
Explications
Recherche des données dans le localStorage associées au produit.
Affichage des images dans une disposition responsive.
Insertion des métadonnées si présentes (texte, police, taille).
3. Suppression des données
Code source
document.addEventListener('DOMContentLoaded', function () {
var deleteButtons = document.querySelectorAll('.remove-from-cart');
deleteButtons.forEach(function (deleteButton) {
deleteButton.addEventListener('click', function () {
var productItem = deleteButton.closest('.cart-item');
var canvasImage = productItem.querySelector('img').src;
if (canvasImage) {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var storedData = JSON.parse(localStorage.getItem(key));
if (storedData && storedData.canvas === canvasImage) {
localStorage.removeItem(key);
break;
}
}
}
productItem.remove();
});
});
});
Explications
Vérifie si l'image du canvas correspond à une entrée du localStorage.
Supprime l'entrée correspondante.
Met à jour le DOM pour refléter la suppression.
Contraintes
Frontend uniquement :
Toutes les opérations sont effectuées côté client, ce qui limite les possibilités (ex. : pas de sauvegarde sur serveur).
Performance :
L'utilisation de localStorage est rapide mais peut être limitée en taille (5 Mo en général).
Responsive Design :
Les images sont redimensionnées dynamiquement pour s'adapter à différentes tailles d'écran.
Code complet
document.addEventListener('DOMContentLoaded', function() {
var saveButton = document.getElementById('cv-saveCompositionButton');
if (saveButton) {
saveButton.addEventListener('click', function() {
var upperCanvas = document.querySelector('canvas.upper-canvas');
var lowerCanvas = document.querySelector('canvas.lower-canvas');
// Capture du contenu du texte, avec une vérification de son existence
var textContentElement = document.getElementById('text-content-line-0');
var textContent = textContentElement ? textContentElement.value : null;
// Capture de la police
var fontFamilyElement = document.querySelector('#font-family-line-0-button .ui-selectmenu-text');
var fontFamily = fontFamilyElement ? fontFamilyElement.textContent : null;
// Capture de la hauteur du texte
var textHeightElement = document.getElementById('textHeightInput-line-0');
var textHeight = textHeightElement ? textHeightElement.value : null;
var url = window.location.href;
var productIdMatch = url.match(/\/(\d+)-/); // Extrait l'ID produit
var productId = productIdMatch ? productIdMatch[1] : null;
if (upperCanvas && lowerCanvas && productId) {
var combinedCanvas = document.createElement('canvas');
combinedCanvas.width = upperCanvas.width;
combinedCanvas.height = upperCanvas.height;
var ctx = combinedCanvas.getContext('2d');
ctx.drawImage(lowerCanvas, 0, 0);
ctx.drawImage(upperCanvas, 0, 0);
setTimeout(function() {
try {
var canvasData = combinedCanvas.toDataURL('image/png');
var uniqueKey = 'canvasImage_' + productId + '_' + Date.now(); // Ajout d'un timestamp pour un identifiant unique
// Préparation de l'objet à stocker dans le localStorage
var dataToStore = { canvas: canvasData };
// Ajout conditionnel des autres propriétés uniquement si elles existent
if (textContent) {
dataToStore.textContent = textContent;
}
if (fontFamily) {
dataToStore.fontFamily = fontFamily;
}
if (textHeight) {
dataToStore.textHeight = textHeight;
}
// Stocker les données dans le localStorage
localStorage.setItem(uniqueKey, JSON.stringify(dataToStore));
console.log('Données sauvegardées pour l\'ID produit ' + productId);
} catch (e) {
console.error('Erreur lors de la capture du canvas :', e);
}
}, 500);
} else {
console.error('Upper-canvas, lower-canvas ou ID produit introuvable.');
}
});
} else {
console.error('Bouton "Ajouter au panier" introuvable.');
}
});
document.addEventListener('DOMContentLoaded', function() {
var cartItems = document.querySelectorAll('#main > div.cart-grid.row > div.cart-grid-body.col-xs-12.col-lg-8 > div > div > ul > li');
var productCanvasMap = {};
cartItems.forEach(function(cartItem, index) {
// Rechercher toutes les étiquettes dans la classe "product-line-info"
var infoLabels = cartItem.querySelectorAll('.product-line-grid-right .product-line-info .label');
var designerRefLabel = null;
// Parcourir les labels pour trouver celui qui contient "designer-ref:"
infoLabels.forEach(function(label) {
if (label.innerText.trim() === 'designer-ref:') {
designerRefLabel = label;
}
});
// Si "designer-ref:" a été trouvé
if (designerRefLabel) {
console.log('Designer-ref trouvé:', designerRefLabel.innerText.trim());
var designerRefValue = designerRefLabel.nextElementSibling ? designerRefLabel.nextElementSibling.innerText : null;
// Extraire l'ID produit après le dernier "-"
var productIdMatch = designerRefValue ? designerRefValue.match(/-(\d+)$/) : null;
var productId = productIdMatch ? productIdMatch[1] : null;
if (productId) {
if (!productCanvasMap[productId]) {
productCanvasMap[productId] = [];
// Parcourir tous les éléments du localStorage et ajouter les clés qui commencent par 'canvasImage_' ou 'canvasOtherImage_'
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (key.startsWith('canvasImage_' + productId) || key.startsWith('canvasOtherImage_' + productId)) {
productCanvasMap[productId].push(key);
}
}
// Trier les clés par timestamp
productCanvasMap[productId].sort(function(a, b) {
var timestampA = parseInt(a.split('_').pop());
var timestampB = parseInt(b.split('_').pop());
return timestampA - timestampB; // Tri du plus ancien au plus récent
});
}
// Associer les canvas dans l'ordre au bon produit
if (productCanvasMap[productId].length > 0) {
var canvasKey = productCanvasMap[productId].shift(); // Prend le plus ancien canvas
var storedData = JSON.parse(localStorage.getItem(canvasKey));
if (storedData) {
var canvasData = storedData.canvas;
var img = document.createElement('img');
img.src = canvasData;
img.alt = 'Aperçu de la personnalisation';
// Utilisation de styles responsives
img.style.width = '100%'; // S'adapte à 100% de la largeur du parent
img.style.maxWidth = '600px'; // Ne dépassera pas 600px sur les écrans plus grands
img.style.height = 'auto'; // Maintient le ratio de l'image
img.style.marginTop = '20px';
var previewDiv = document.createElement('div');
previewDiv.id = 'canvas-image-preview-' + canvasKey;
previewDiv.appendChild(img);
var targetElement = cartItem.querySelector('div.product-line-grid-right.product-line-actions.ms-auto.mt-20.col-md-10.col-xs-12');
// Vérifier si l'élément de destination existe
if (targetElement) {
targetElement.appendChild(previewDiv);
// Créer un élément texteInfo pour afficher les informations supplémentaires (si disponibles)
var textInfo = document.createElement('p');
if (storedData.textContent) {
textInfo.innerHTML += `<strong>Texte :</strong> ${storedData.textContent}<br>`;
}
if (storedData.fontFamily) {
textInfo.innerHTML += `<strong>Police :</strong> ${storedData.fontFamily}<br>`;
}
if (storedData.textHeight) {
textInfo.innerHTML += `<strong>Hauteur :</strong> ${storedData.textHeight} cm<br>`;
}
// Ajouter les informations texte si elles existent
if (textInfo.innerHTML !== '') {
targetElement.appendChild(textInfo);
}
} else {
console.error('L\'élément cible pour insérer le canvas n\'a pas été trouvé.');
}
}
} else {
console.error('Aucun canvas disponible pour le produit avec ID ' + productId);
}
} else {
console.error('ID produit introuvable dans le panier.');
}
} else {
console.error('Élément designer-ref introuvable pour cet article.');
}
});
});
document.addEventListener('DOMContentLoaded', function () {
var deleteButtons = document.querySelectorAll('.remove-from-cart');
deleteButtons.forEach(function (deleteButton) {
deleteButton.addEventListener('click', function (event) {
event.preventDefault();
var productItem = deleteButton.closest('.cart-item');
// Rechercher l'image du canvas et les données de personnalisation
var canvasImage = productItem.querySelector('.product-line-grid-right img').src;
if (canvasImage) {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var storedData = JSON.parse(localStorage.getItem(key));
// Vérifier que les données stockées contiennent bien un canvas et que l'image correspond
if (storedData && storedData.canvas && storedData.canvas === canvasImage) {
localStorage.removeItem(key); // Supprimer l'élément du localStorage
console.log('Canvas et données supprimés pour la clé : ' + key);
break;
}
}
} else {
console.error("Canvas non trouvé pour ce produit.");
}
// Supprimer l'élément du DOM
productItem.remove();
// Réinitialiser le overflow du body à auto
document.body.style.overflow = "auto";
// Effectuer la suppression via AJAX
var deleteUrl = deleteButton.getAttribute('href');
fetch(deleteUrl, {
method: 'GET' // ou 'POST' si nécessaire selon votre serveur
})
.then(function(response) {
if (response.ok) {
// Rediriger vers le panier après suppression
window.location.replace("https://www.lacier.fr/panier?show");
} else {
console.error('Erreur lors de la suppression du produit');
}
})
.catch(function(error) {
console.error('Erreur réseau :', error);
});
});
});
});
Emplacement du fichier
Le code du module est stocké dans le fichier suivant :
Structure claire : Le fichier est placé dans le répertoire assets/js pour séparer les scripts personnalisés des autres fichiers.
Thème enfant : Utilisation du dossier classic-child pour préserver les modifications lors des mises à jour du thème principal.
Facilité d'accès : Ce chemin permet d'accéder facilement au fichier depuis le thème actif et de le référencer dans les fichiers .tpl ou le code HTML.
Chargement dans le site :
Le fichier est inclus via le système de PrestaShop, souvent dans le fichier header.tpl ou via un mécanisme d'injection dans le back-office.