Génération d'images à partir de WXML dans les Mini-Programmes Uni-app

Ce guide présente une méthode pour générer des images à partir de descriptions WXML dans le cadre d'une application Uni-app destinée aux mini-programmes. L'objectif est de permettre la sauvegarde de contenu de page sous forme d'image pour une éventuelle téléversement.

Une première tenttaive avec html-to-canvas s'est heurtée à la restriction des mini-programmes : l'impossibilité de manipuler directement le DOM ou d'y accéder sans passer par des méthodes spécifiques comme wx.createSelectorQuery().

La soluiton native des mini-programmes WeChat, wxml-to-canvas, bien que conçue pour générer des images à partir de Canvas WXML, présente une configuration spécifique aux mini-programmes natifs, ce qui pose des problèmes d'intégration avec Uni-app.

Intégration de wxml-to-canvas dans Uni-app

  1. Téléchargez l'exemple de code fourni par le mini-programme WeChat officiel.

  2. Dans le répertoire racine de votre projet Uni-app, créez un dossier wxcomponents. Copiez-y les dossiers widget-ui et wxml-to-canvas provenant de la section miniprogram_npm de l'exemple.

  3. Modifiez le fichier /wxcomponents/wxml-to-canvas/index.js comme suit :

    
    // Ligne originale :
    // module.exports = require("widget-ui");
    // Ligne modifiée :
    module.exports = require("../widget-ui/index.js");
    
    
  4. Configurez votre fichier pages.json pour que Uni-app inclue correctement le dosier wxcomponents lors du packaging :

    
    {
       "pages": [
           {
               "path": "pages/share",
               "style": {
                   "usingComponents": {
                       "wxml-to-canvas": "/wxcomponents/wxml-to-canvas/index"
                   }
               }
           }
           // ... autres pages
       ]
    }
    
    
  5. Créez ou modifiez votre page principale, par exemple pages/share.vue :

    
    <template>
       <view class="share-page">
           <view class="share-page-box" id="box" v-if="isCanvasVisible"
               :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }">
               <wxml-to-canvas class="widget-renderer" :width="canvasWidth" :height="canvasHeight"></wxml-to-canvas>
           </view>
           <view class="share-page-btn" @tap="saveAsImage">
               <button class="btn-big" :style="getButtonStyle">Enregistrer l'image</button>
           </view>
       </view>
    </template>
    
    <script>
       import { generateWxmlContent, generateStyleSheet } from './contentGenerator.js';
    
       export default {
           data() {
               return {
                   isCanvasVisible: false,
                   canvasWidth: 320,
                   canvasHeight: 480,
                   deviceScreenWidth: null,
                   deviceScreenHeight: null,
                   titleText: '',
                   imageUrl: '',
                   chapterList: [],
                   canvasComponent: null,
               };
           },
           onLoad(options) {
               console.log('Options reçues :', options);
               this.titleText = 'Génération Canvas via WXML (Exemple)';
               this.imageUrl = 'https://via.placeholder.com/150'; // Remplacez par une URL d'image valide
               this.chapterList = ['Chapitre 1', 'Chapitre 2', 'Chapitre 3']; // Liste d'exemples
    
               // Récupérer les informations système
               wx.getSystemInfo({
                   success: (res) => {
                       console.log("Informations système :", res);
                       this.deviceScreenWidth = res.screenWidth;
                       // Il est recommandé de calculer ou de fixer la hauteur pour éviter les problèmes de dépassement de texte.
                       this.deviceScreenHeight = 1000; 
                       this.canvasWidth = this.deviceScreenWidth;
                       this.canvasHeight = this.deviceScreenHeight;
                       this.isCanvasVisible = true;
    
                       // Ajustement de la largeur du conteneur pour le rendu dynamique
                       setTimeout(() => {
                           wx.showLoading({ title: 'Génération de l\'image...' });
                           // Sélection du composant wxml-to-canvas
                           this.canvasComponent = this.selectComponent('.widget-renderer');
                           this.renderContentToCanvas();
                       }, 1000);
                   }
               });
           },
           methods: {
               // Rendu WXML vers Canvas
               renderContentToCanvas() {
                   console.log('Composant Canvas :', this.canvasComponent);
                   const dynamicWxml = generateWxmlContent(this.titleText, this.imageUrl, this.chapterList);
                   const dynamicStyle = generateStyleSheet(this.deviceScreenWidth, this.canvasWidth, this.canvasHeight);
    
                   const renderPromise = this.canvasComponent.renderToCanvas({
                       wxml: dynamicWxml,
                       style: dynamicStyle,
                   });
    
                   renderPromise.then((res) => {
                       console.log('Rendu Canvas réussi');
                       wx.hideLoading();
                   }).catch((err) => {
                       console.error('Erreur lors du rendu Canvas :', err);
                       wx.hideLoading();
                   });
               },
               // Enregistrement de l'image
               saveAsImage() {
                   const capturePromise = this.canvasComponent.canvasToTempFilePath();
                   capturePromise.then(result => {
                       const imagePath = result.tempFilePath;
                       console.log('Chemin temporaire de l\'image :', imagePath);
                       // Ici, vous pouvez utiliser imagePath pour le téléversement ou d'autres actions.
                   }).catch(err => {
                       console.error('Erreur lors de la capture du Canvas :', err);
                   });
               },
           }
       };
    </script>
    
    
  6. Créez un nouveau fichier, par exemple pages/contentGenerator.js, pour définir le contenu WXML et le style :

    
    /**
    * Génère le contenu WXML sous forme de chaîne de caractères.
    * @param {string} title Le titre à afficher.
    * @param {string} imgSrc L'URL de l'image.
    * @param {string[]} chapterData Les éléments de chapitre.
    * @returns {string} La chaîne WXML générée.
    */
    const generateWxmlContent = (title, imgSrc, chapterData) => `
    <view class="container">
       <image src="${imgSrc}" class="profile-pic"/>
       <text class="title-text">${title}</text>
       ${chapterData.map((chapter, index) => `
       <text class="chapter-content">${chapter}</text>
       `).join('')}
    </view>
    `;
    
    /**
    * Génère les styles pour le Canvas.
    * @param {number} screenWidth La largeur de l'écran du périphérique.
    * @param {number} canvasWidth La largeur du Canvas.
    * @param {number} canvasHeight La hauteur du Canvas.
    * @returns {object} L'objet de styles.
    */
    const generateStyleSheet = (screenWidth, canvasWidth, canvasHeight) => {
       return {
           "container": {
               width: canvasWidth,
               height: canvasHeight,
               position: 'relative',
               overflow: 'hidden',
               backgroundColor: '#f0f0f0', // Couleur de fond légère
           },
           "title-text": {
               fontSize: 24, // Taille de police plus grande pour le titre
               color: '#1a1a1a', // Couleur de texte plus foncée
               marginTop: canvasWidth * 0.1,
               marginBottom: canvasWidth * 0.05,
               width: canvasWidth * 0.8,
               textAlign: 'center',
               fontWeight: 'bold', // Ajout de gras pour le titre
           },
           "chapter-content": {
               fontSize: 16, // Taille de police pour le contenu
               color: '#4d4d4d', // Couleur de texte un peu plus claire
               width: canvasWidth * 0.8,
               marginBottom: screenWidth * 0.03, // Espacement ajusté
               marginLeft: canvasWidth * 0.1,
               lineHeight: 24, // Hauteur de ligne pour une meilleure lisibilité
           },
           "profile-pic": {
               width: canvasWidth * 0.4,
               height: screenWidth * 0.3, // Hauteur proportionnelle à la largeur
               marginTop: canvasWidth * 0.1,
               marginLeft: canvasWidth * 0.3,
               marginBottom: canvasWidth * 0.08,
               borderRadius: screenWidth * 0.05, // Coin légèrement arrondi
               overflow: 'hidden',
               border: '2px solid #cccccc', // Bordure légère
           },
       };
    };
    
    module.exports = {
       generateWxmlContent,
       generateStyleSheet
    };
    
    

    Il est important de noter que le WXML retourné par generateWxmlContent est une chaîne de caractères représentant le rendu final. Les directives comme wx:if ou wx:for ne sont pas directement supportées. Pour implémenter des logiques conditionnelles ou des boucles, vous devez les gérer en JavaScript dans la fonction de génération de chaîne WXML.

  7. Rendu final et considérations :

    • wxml-to-canvas prend en charge uniquement les éléments view, text et image.
    • Il est impératif de définir les propriétés width et height pour chaque élément rendu.
    • Par défaut, la disposition des éléments est gérée via Flexbox. Vous pouvez modifier l'orientation avec la propriété flexDirection: "column".

Étiquettes: uniapp mini program Canvas wxml-to-canvas image generation

Publié le 11 juillet à 00h40