oshiphoto

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

EditorCanvas.vue (6462B)


      1 <template>
      2   <div>
      3     <canvas ref="canvasElement"></canvas>
      4   </div>
      5 </template>
      6 
      7 <script setup>
      8 import { ref, onMounted } from 'vue';
      9 import { Canvas, FabricImage, StaticCanvas } from 'fabric';
     10 
     11 const canvasElement = ref(null);
     12 let fabricCanvas = null;
     13 
     14 onMounted(() => {
     15   if (canvasElement.value) {
     16     fabricCanvas = new Canvas(canvasElement.value, {
     17       width: 1,
     18       height: 1,
     19       backgroundColor: 'transparent'
     20     });
     21     console.log('Fabric.jsのCanvasを初期化しました');
     22   } else {
     23     console.error('onMounted: canvasElement が見つかりません');
     24   }
     25 });
     26 
     27 const addOshiImage = (imageUrl) => {
     28   if (!fabricCanvas) return;
     29   console.log('addOshiImage が呼ばれました');
     30   
     31   const imgElement = new Image();
     32   imgElement.onload = () => {
     33     console.log('推し画像の読み込み成功 (Image.onload)');
     34     const fabricImg = new FabricImage(imgElement);
     35     
     36     // Canvasのサイズに対して、いい感じのサイズに調整
     37     // (例: Canvasの幅の 1/3 サイズにする)
     38     const scale = (fabricCanvas.width * 0.33) / imgElement.naturalWidth;
     39     
     40     fabricImg.set({
     41       left: 100,
     42       top: 100,
     43       scaleX: scale,
     44       scaleY: scale,
     45     });
     46     
     47     fabricCanvas.add(fabricImg);
     48     fabricCanvas.renderAll();
     49     console.log('「推し」画像を追加しました');
     50   };
     51   imgElement.onerror = (err) => { /* ... (省略) ... */ };
     52   imgElement.src = imageUrl;
     53 };
     54 
     55 const resizeAndSetBackground = (dataUrl, imgElement, containerWidth) => {
     56   if (!fabricCanvas) {
     57     console.error('resizeAndSetBackground: fabricCanvas が初期化されていません');
     58     return;
     59   }
     60   console.log('resizeAndSetBackground が呼ばれました');
     61 
     62   // 1. 新しいCanvasのサイズを計算
     63   const imgRatio = imgElement.naturalHeight / imgElement.naturalWidth;
     64   
     65   // 親コンテナの幅(containerWidth)をそのままCanvasの幅として使用します
     66   const newWidth = containerWidth;
     67   const newHeight = newWidth * imgRatio;
     68 
     69   // 2. Fabric.jsのCanvasにサイズをセット
     70   fabricCanvas.setDimensions({
     71     width: newWidth,
     72     height: newHeight
     73   });
     74   
     75   // 3. <img> から FabricImage を作成
     76   const fabricImg = new FabricImage(imgElement);
     77 
     78   // 4. 背景としてセット
     79   fabricCanvas.backgroundColor = 'transparent'; // 以前の背景色をクリア
     80   fabricCanvas.backgroundImage = fabricImg;
     81   
     82   // 5. 画像をCanvasの幅にフィットさせる
     83   fabricImg.scaleToWidth(newWidth);
     84 
     85   // 6. 変更をCanvasに反映
     86   fabricCanvas.renderAll();
     87 
     88   console.log(`Canvasをリサイズしました: ${newWidth} x ${newHeight}`);
     89 };
     90 
     91 const deleteActiveObject = () => {
     92   if (!fabricCanvas) return;
     93   const activeObject = fabricCanvas.getActiveObject();
     94   if (activeObject) {
     95     fabricCanvas.remove(activeObject);
     96     fabricCanvas.renderAll();
     97   }
     98 };
     99 
    100 const exportOriginalSizeDataURL = (originalImageElement) => {
    101   try {
    102     if (!fabricCanvas || !originalImageElement) {
    103       console.error('exportOriginalSizeDataURL: 引数が不足しています');
    104       return null;
    105     }
    106 
    107     // 1. オリジナル解像度
    108     const originalWidth = originalImageElement.naturalWidth;
    109     const originalHeight = originalImageElement.naturalHeight;
    110 
    111     // 2. 現在の表示サイズ
    112     const currentWidth = fabricCanvas.width;
    113 
    114     // 3. スケーリング比率
    115     const ratio = originalWidth / currentWidth;
    116     
    117     if (ratio <= 0 || !isFinite(ratio)) {
    118       console.error('無効なスケーリング比率です', ratio);
    119       return null;
    120     }
    121 
    122     // 4. メモリ上に元サイズの StaticCanvas を作成
    123     const staticCanvas = new StaticCanvas(null, {
    124       width: originalWidth,
    125       height: originalHeight
    126     });
    127 
    128     // 5. 背景画像を手動で「再構築」
    129     const bgImage = fabricCanvas.backgroundImage;
    130     if (bgImage) {
    131       // getElement() で <img> 要素を取得
    132       const bgImgElement = bgImage.getElement();
    133       const newBgScale = bgImage.scaleX * ratio;
    134       
    135       // <img> 要素から新しい FabricImage を作成
    136       const newBg = new FabricImage(bgImgElement, {
    137         scaleX: newBgScale,
    138         scaleY: newBgScale
    139       });
    140       staticCanvas.backgroundImage = newBg;
    141     }
    142 
    143     // 6. オブジェクト(推し)を個別に「再構築」
    144     const objects = fabricCanvas.getObjects();
    145     if (objects.length > 0) {
    146       objects.forEach(obj => {
    147         // 7. <img> 要素を取得 (FabricImage のみ)
    148         if (obj.type === 'image' && obj.getElement) {
    149           const oshiImgElement = obj.getElement();
    150           
    151           // 8. <img> とスケーリング済みのプロパティから新しい FabricImage を作成
    152           const newOshi = new FabricImage(oshiImgElement, {
    153             // スケーリングするプロパティ
    154             left: obj.left * ratio,
    155             top: obj.top * ratio,
    156             scaleX: obj.scaleX * ratio,
    157             scaleY: obj.scaleY * ratio,
    158             
    159             // そのままコピーするプロパティ
    160             angle: obj.angle,
    161             flipX: obj.flipX,
    162             flipY: obj.flipY,
    163             skewX: obj.skewX,
    164             skewY: obj.skewY,
    165             originX: obj.originX,
    166             originY: obj.originY,
    167             // ... (他にも必要なプロパティがあればここに追加)
    168           });
    169           
    170           // 9. StaticCanvas に追加
    171           staticCanvas.add(newOshi);
    172         }
    173       });
    174     }
    175 
    176     // 10. 最終描画
    177     staticCanvas.renderAll();
    178 
    179     // 11. 高解像度のData URLを生成
    180     const dataUrl = staticCanvas.toDataURL({
    181       format: 'png',
    182       quality: 1.0
    183     });
    184 
    185     // 12. メモリ解放
    186     staticCanvas.dispose();
    187 
    188     return dataUrl; // ★ 成功パス
    189 
    190   } catch (error) {
    191     console.error('exportOriginalSizeDataURL 内部でエラー:', error);
    192     return null; // ★ 失敗パス
    193   }
    194 };
    195 
    196 defineExpose({
    197   resizeAndSetBackground,
    198   addOshiImage,
    199   deleteActiveObject,
    200   exportOriginalSizeDataURL
    201 });
    202 </script>
    203 
    204 <style scoped>
    205 /*
    206   <canvas> タグを囲むラッパー(自動生成)に
    207   スタイルを適用するため :deep() を使います
    208 */
    209 :deep(.canvas-container) {
    210   /*
    211     親要素 (App.vueの .canvas-wrapper) の
    212     サイズに追従するようにします
    213   */
    214   max-width: 100%;
    215   max-height: 100%;
    216 
    217   /* スマホ画面で縮小表示されたときもくっきりさせる */
    218   image-rendering: -webkit-optimize-contrast;
    219   image-rendering: crisp-edges;
    220 }
    221 </style>