寫扣職人
    首頁課程商品文章影片登入 / 註冊
    返回系列
    【前端動手玩創意】不無聊的HTML與CSS指南
    免費

    不無聊的前端切版課:第10章──卡片製作,才不是!是卡片製作器!

    如果你想學網頁,那歡迎你來對地方了。 這個指南手冊除了非常適合新手以外,還超級有趣,絕對靠北又能學到東西。

    切板職人
    4 分鐘
    2025年12月12日

    前情提要

    常常看到好看的卡片效果,今天我們就來寫一個hero section吧!
    但是……等一下什麼!你說你已經看到標題!
    沒錯!已經來到前端動手玩創意系列的第十篇,我們累積了那麼多,特別的第十篇才不要帶你寫「卡片製作」,
    我們要來搞一個card maker,呼應了大前端時代模組化的風格(๑ • ‿ • ๑ )。

    到底什麼是製作你的製作呢?我們來看看效果:

    效果預覽

    點擊放大

    沒錯就是這樣子,可以輸入圖片網址、讓人看不懂的日文(我也不懂那啥意思XD)
    就可以跳出一張很有感覺的hero section!
    甚至還寫了調整圖片的移動軸:

    點擊放大

    還可以下載!?這真的是太讚了,完全就是實現動手玩創意的絕佳精神呀~~

    程式碼內容

    <!DOCTYPE html>
    <html>
      <head>
        <title>Card Maker</title>
        <style>
          body {
            display: flex;
            flex-direction: row;
          }
    
          .input-container {
            display: flex;
            flex-direction: column;
            margin-right: 20px;
          }
    
          .input-container label {
            margin-bottom: 10px;
          }
    
          .card-container {
            position: relative;
            width: 600px;
            height: 600px;
            margin-right: 20px;
          }
    
          .card-container canvas {
            position: absolute;
            top: 0;
            left: 0;
          }
    
          .download-button {
            margin-top: 10px;
          }
    
          .ui-panel {
            display: flex;
            flex-direction: column;
          }
    
          .ui-panel label {
            margin-bottom: 10px;
          }
    
          .ui-panel input[type="range"] {
            margin-bottom: 20px;
          }
    
          .hero-image {
            position: relative;
            width: 100%;
            height: 100%;
            background-size: cover;
            background-position: center;
          }
    
          .hero-text {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            text-align: center;
            color: white;
          }
        </style>
      </head>
      <body>
        <div class="input-container">
          <label for="image-input">Image URL:</label>
          <input type="text" id="image-input" />
          <label for="text-input">Text:</label>
          <input type="text" id="text-input0" />
          <input type="text" id="text-input" />
          <button id="confirm-button">Confirm</button>
        </div>
    
        <div class="card-container">
          <div class="hero-image">
            <div class="hero-text">
              <h1 id="card-title"></h1>
              <p id="card-text"></p>
            </div>
          </div>
    
          <div class="ui-panel">
            <label for="brightness-slider">亮度</label>
            <input
              type="range"
              id="brightness-slider"
              min="-1"
              max="1"
              value="0"
              step="0.1"
            />
    
            <label for="saturation-slider">飽和度</label>
            <input
              type="range"
              id="saturation-slider"
              min="-1"
              max="1"
              value="0"
              step="0.1"
            />
    
            <label for="size-slider">大小</label>
            <input
              type="range"
              id="size-slider"
              min="0.5"
              max="2"
              value="1"
              step="0.1"
            />
          </div>
    
          <button class="download-button" id="download-button">Download</button>
    
          <canvas id="card-canvas"></canvas>
        </div>
    
        <script>
          const imageInput = document.getElementById("image-input");
          const textInput = document.getElementById("text-input");
          const textInput0 = document.getElementById("text-input0");
          const confirmButton = document.getElementById("confirm-button");
          const cardTitle = document.getElementById("card-title");
          const cardText = document.getElementById("card-text");
          const downloadButton = document.getElementById("download-button");
          const cardCanvas = document.getElementById("card-canvas");
    
          confirmButton.addEventListener("click", () => {
            // Update the card title and text
            cardTitle.innerText = textInput0.value;
            cardText.innerText = textInput.value;
    
            // Update the hero image background
            const imageUrl = imageInput.value;
            const heroImage = document.querySelector(".hero-image");
            heroImage.style.backgroundImage = `linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url("${imageUrl}")`;
    
            // Generate the card image
            const cardImage = new Image();
    
            cardImage.onload = () => {
              // Get the canvas context and set its dimensions
    const ctx = cardCanvas.getContext('2d');
              const canvasWidth = (cardCanvas.width =
                heroImage.clientWidth * parseFloat(sizeSlider.value));
              const canvasHeight = (cardCanvas.height =
                heroImage.clientHeight * parseFloat(sizeSlider.value));
    
              // Draw the hero image onto the canvas
    ctx.drawImage(heroImage, 0, 0, cardCanvas.width, cardCanvas.height);
    
    
              // Apply brightness and saturation filters to the canvas
              applyFilters(
                context,
                parseFloat(brightnessSlider.value),
                parseFloat(saturationSlider.value)
              );
    
              // Update the download button href attribute with the canvas data URL
              downloadButton.href = cardCanvas.toDataURL("image/png");
            };
          });
    
          function applyFilters(context, brightness, saturation) {
            // Convert brightness and saturation values to filter functions
            const brightnessFilter = createBrightnessFilter(brightness);
            const saturationFilter = createSaturationFilter(saturation);
    
            // Apply the filters to the canvas
            context.filter = `${brightnessFilter} ${saturationFilter}`;
            context.drawImage(cardCanvas, 0, 0);
            context.filter = "none";
          }
    
          function createBrightnessFilter(value) {
            // Use a matrix filter to adjust brightness
            const brightnessMatrix = [
              1,
              0,
              0,
              0,
              value,
              0,
              1,
              0,
              0,
              value,
              0,
              0,
              1,
              0,
              value,
              0,
              0,
              0,
              1,
              0,
            ];
    
            return `brightness(${value})`;
          }
    
          function createSaturationFilter(value) {
            // Use a matrix filter to adjust saturation
            const saturationMatrix = [
              0.3086 * (1 - value) + value,
              0.6094 * (1 - value),
              0.082 * (1 - value),
              0,
              0,
              0.3086 * (1 - value),
              0.6094 * (1 - value) + value,
              0.082 * (1 - value),
              0,
              0,
              0.3086 * (1 - value),
              0.6094 * (1 - value),
              0.082 * (1 - value) + value,
              0,
              0,
              0,
              0,
              0,
              1,
              0,
            ];
    
            return `saturate(${value})`;
          }
    
          const brightnessSlider = document.getElementById("brightness-slider");
          const saturationSlider = document.getElementById("saturation-slider");
          const sizeSlider = document.getElementById("size-slider");
    
          brightnessSlider.addEventListener("input", () => {
            applyFilters(
              cardCanvas.getContext("2d"),
              parseFloat(brightnessSlider.value),
              parseFloat(saturationSlider.value)
            );
          });
    
          saturationSlider.addEventListener("input", () => {
            applyFilters(
              cardCanvas.getContext("2d"),
              parseFloat(brightnessSlider.value),
              parseFloat(saturationSlider.value)
            );
          });
    
          sizeSlider.addEventListener("input", () => {
            cardCanvas.width = heroImage.clientWidth * parseFloat(sizeSlider.value);
            cardCanvas.height =
              heroImage.clientHeight * parseFloat(sizeSlider.value);
            applyFilters(
              cardCanvas.getContext("2d"),
              parseFloat(brightnessSlider.value),
              parseFloat(saturationSlider.value)
            );
          });
          
          const image = document.querySelector('.hero-image');
    
    brightnessSlider.addEventListener('change', () => {
      image.style.filter = `brightness(${brightnessSlider.value}) saturate(${saturationSlider.value})`;
    });
    
    saturationSlider.addEventListener('change', () => {
      image.style.filter = `brightness(${brightnessSlider.value}) saturate(${saturationSlider.value})`;
    });
    
    sizeSlider.addEventListener('change', () => {
      image.style.transform = `scale(${sizeSlider.value})`;
    });
    const canvas = document.getElementById('card-canvas');
    
         downloadButton.addEventListener('click', () => {
      const link = document.createElement('a');
      link.download = 'image.png';
      link.href = canvas.toDataURL();
      link.click();
    });
        </script>
      </body>
    </html>
    
    

    觀念筆記

    目前代碼的部分canvas還沒轉好,晚點應該會修正為用html2canvas套件來抓。
    不過可以先玩玩看卡片製作的部分唷!

    心得

    很有趣的小項目,其實學前端什麼專業知識、構造函數、模組那些都太無聊了,
    講真的如果只是自己做開心的,
    那用什麼技術與概念又何妨呢是吧?人生就是輕鬆玩樂不需要一直給自己找碴,生活還不夠苦嗎XD
    前端最有趣的就是你怎麼寫,畫面就怎麼回饋給你,
    比這個人生online還要好操控多惹d(`・∀・)b

    就當作遊戲來跟著這個系列探索世界吧!!
    請繼續關注,
    跟著我們這個系列繼續動手玩創意!!未來還有更多更新鮮的花招呢(。◕∀◕。)

    0 瀏覽

    留言區

    登入後即可留言

    登入 / 註冊
    返回系列文章
    寫扣職人
    隱私權政策使用者條款購買與退款須知

    專業的線上學習平台,提供高品質課程內容

    © 2026 寫扣職人. All rights reserved.

    Powered by Classroo