给夫人送一朵玫瑰!

给夫人送一朵玫瑰!

为了满足我的爱好,有趣又好玩,而且还要应夫人要求不花钱,所以我打算给夫人赠送一朵赛博玫瑰!

编写代码

首先要有一朵花,同时加一些文字效果。

代码如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>300天纪念日</title>
  <meta name="language" content="zh-CN">
  <meta name="title" content="300天纪念日">
  <link rel="icon" type="image/x-icon" href="heart.png">

  <style>
    .container {
      background: rgb(255, 192, 203);
      color: white;
      text-align: center;
      width: 100%;
      height: 100vh;
      position: relative;
      overflow: hidden;
    }

    .canvas-container {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      width: 45vmin;
      height: 45vmin;
      animation: slideIn 2s ease-out forwards;
    }

    @keyframes slideIn {
      from { 
        left: 150%;
        transform: translate(-50%, -50%);
      }
      to { 
        left: 50%;
        transform: translate(-50%, -50%);
      }
    }

    .word {
      position: absolute;
      left: 50%;
      top: 20%;
      transform: translateX(-50%);
      font-family: 楷体;
      font-size: 5vmin;
      color: #c70012;
      opacity: 0;
      animation: fadeIn 3s ease-in forwards;
      animation-delay: 2s;
      white-space: nowrap; /* Prevent line breaks */
    }

    .poem {
      position: absolute;
      left: -50%;
      top: 40%;
      transform: translateY(-50%);
      font-family: 楷体;
      font-size: 5vmin;
      color: #c70012;
      animation: slideInPoem 2s ease-out forwards;
      animation-delay: 2s;
    }

    @keyframes slideInPoem {
      from { left: -50%; }
      to { left: 10%; }
    }

    .poem span {
      display: block;
      opacity: 0;
      margin: 2vh 0;
    }

    .poem span:nth-child(1) {
      animation: fadeInPoem 2s ease-in forwards;
      animation-delay: 2.5s;
    }

    .poem span:nth-child(2) {
      animation: fadeInPoem 2s ease-in forwards;
      animation-delay: 3.5s;
    }

    @keyframes fadeIn {
      from { opacity: 0; }
      to { opacity: 1; }
    }

    @keyframes fadeInPoem {
      from { 
        opacity: 0;
        transform: translateX(-2vw);
      }
      to { 
        opacity: 1;
        transform: translateX(0);
      }
    }

    canvas {
      width: 100%;
      height: 100%;
      opacity: 0;
      animation: bloomIn 4s ease-in forwards;
    }

    @keyframes bloomIn {
      0% { 
        opacity: 0;
        transform: scale(0.3);
      }
      100% { 
        opacity: 1;
        transform: scale(1);
      }
    }

    .date-clock {
      position: absolute;
      left: 50%;
      top: 80%;
      transform: translateX(-50%);
      font-family: 楷体;
      font-size: 4vmin;
      color: #c70012;
      opacity: 0;
      animation: slideInClock 2s ease-out forwards,
                 fadeIn 2s ease-in forwards;
      animation-delay: 2.5s;
      white-space: nowrap; /* Prevent line breaks */
    }

    @keyframes slideInClock {
      from { 
        left: -50%;
        transform: translateX(-50%);
      }
      to { 
        left: 50%;
        transform: translateX(-50%);
      }
    }

    .heart {
      position: fixed;
      font-size: 2vmin;
      color: rgba(199, 0, 18, 0.5);
      animation: fall linear;
      z-index: -1;
    }

    @keyframes fall {
      0% {
        transform: translateY(-100vh) translateX(0) rotate(0deg);
      }
      100% {
        transform: translateY(100vh) translateX(20px) rotate(360deg);
      }
    }

    .audio-control {
      position: fixed;
      bottom: 20px;
      right: 20px;
      z-index: 100;
      background: rgba(255, 255, 255, 0.8);
      border: none;
      border-radius: 50%;
      width: 40px;
      height: 40px;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 20px;
      opacity: 0;
      animation: fadeIn 1s ease-in forwards;
      animation-delay: 1s;
    }

    .audio-control:hover {
      background: rgba(255, 255, 255, 1);
    }
  </style>
</head>
  

<body class="container">
  <audio id="bgMusic" loop preload="auto">
    <source src="fire.mp3" type="audio/mp3">
  </audio>

  <div class="date-clock"></div>

  <div class="poem">
    <span>人生路漫漫</span>
    <span>有你真好</span>
  </div>
  <div class="word">浪漫至死不渝</div>
  <div class="canvas-container">
    
    <canvas id="c"></canvas>
  </div>
    
  <script>  
    var b = document.body;
    var c = document.getElementsByTagName('canvas')[0];
    var a = c.getContext('2d');
    
    // Set canvas size based on its container
    function resizeCanvas() {
      var container = c.parentElement;
      c.width = container.offsetWidth;
      c.height = container.offsetHeight;
      f = c.width; // Update f to match new canvas size
    }
    
    // Resize on load and window resize
    resizeCanvas();
    window.addEventListener('resize', resizeCanvas);
  </script>  
  <script>  
    with (m = Math) {
        C = cos;
        S = sin;
        P = pow;
        R = random;
    }
    c.width = c.height = f = 600;
    h = -250;

    // Add rendering control variables
    const TOTAL_TIME = 10000; // 20 seconds in milliseconds
    const POINTS_PER_FRAME = 10000; // Points to draw per frame
    let startTime = Date.now();
    let progress = 0;

    function p(a, b, c) {
        if (c > 60) {
            return [
                S(a * 7) * (13 + 5 / (.2 + P(b * 4, 4))) - S(b) * 50,
                b * f + 50,
                625 + C(a * 7) * (13 + 5 / (.2 + P(b * 4, 4))) + b * 400,
                a * 1 - b / 2,
                a
            ];
        }

        A = a * 2 - 1;
        B = b * 2 - 1;

        if (A * A + B * B < 1) {
            if (c > 37) {
                n = (j = c & 1) ? 6 : 4;
                o = .5 / (a + .01) + C(b * 125) * 3 - a * 300;
                w = b * h;
                return [
                    o * C(n) + w * S(n) + j * 610 - 390,
                    o * S(n) - w * C(n) + 550 - j * 350,
                    1180 + C(B + A) * 99 - j * 300,
                    .4 - a * .1 + P(1 - B * B, -h * 6) * .15 - a * b * .4 + C(a + b) / 5 + P(C((o * (a + 1) + (B > 0 ? w : -w)) / 25), 30) * .1 * (1 - B * B),
                    o / 1e3 + .7 - o * w * 3e-6
                ];
            }

            if (c > 32) {
                c = c * 1.16 - .15;
                o = a * 45 - 20;
                w = b * b * h;
                z = o * S(c) + w * C(c) + 620;
                return [
                    o * C(c) - w * S(c),
                    28 + C(B * .5) * 99 - b * b * b * 60 - z / 2 - h,
                    z,
                    (b * b * .3 + P((1 - (A * A)), 7) * .15 + .3) * b,
                    b * .7
                ];
            }

            o = A * (2 - b) * (80 - c * 2);
            w = 99 - C(A) * 120 - C(b) * (-h - c * 4.9) + C(P(1 - b, 7)) * 50 + c * 2;
            z = o * S(c) + w * C(c) + 700;
            return [
                o * C(c) - w * S(c),
                B * 99 - C(P(b, 7)) * 50 - c / 3 - z / 1.35 + 450,
                z,
                (1 - b / 1.2) * .9 + a * .1,
                P((1 - b), 20) / 4 + .05
            ];
        }
    }

    function draw() {
        // Calculate progress (0 to 1)
        progress = Math.min((Date.now() - startTime) / TOTAL_TIME, 1);
        
        // Calculate how many points to draw in this frame
        const maxPoints = Math.floor(10000 * progress); // Total points scaled by progress
        
        // Draw points for this frame
        for(i = 0; i <= Math.min(POINTS_PER_FRAME, maxPoints); i++) {
            if(s = p(R(), R(), i % 46 / .74)) {
                z = s[2];
                x = ~~(s[0] * f / z - h);
                y = ~~(s[1] * f / z - h);
                if(!m[q = y * f + x] || m[q] > z) {
                    m[q] = z;
                    a.fillStyle = "rgb(" + 
                        ~(s[3] * h) + "," +
                        ~(s[4] * h) + "," +
                        ~(s[3] * s[3] * -80) + 
                    ")";
                    a.fillRect(x, y, 1, 1);
                }
            }
        }

        // Continue animation if not complete
        if (progress < 1) {
            requestAnimationFrame(draw);
        }
    }

    // Start the drawing animation
    requestAnimationFrame(draw);
  </script>

  <script>
    // Add date clock update function
    function updateClock() {
      const startDate = new Date('2024-03-12T07:39:00');
      const now = new Date();
      const diff = now - startDate;
      
      // Calculate days, hours
      const days = Math.floor(diff / (1000 * 60 * 60 * 24));
      const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
      const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
      const seconds = Math.floor((diff % (1000 * 60)) / 1000);

      // Update clock text
      const clockElement = document.querySelector('.date-clock');
      clockElement.textContent = `和你在一起的第${days}天${hours}小时${minutes}分钟${seconds}秒`;
    }

    // Update clock immediately and every second
    updateClock();
    setInterval(updateClock, 1000);
  </script>

  <script>
    // Heart rain animation
    function createHeart() {
      const heart = document.createElement('div');
      heart.className = 'heart';
      heart.innerHTML = '❤';
      
      // Random starting position
      heart.style.left = Math.random() * 100 + 'vw';
      
      // Random animation duration (3-8 seconds)
      const duration = 3 + Math.random() * 5;
      heart.style.animationDuration = duration + 's';
      
      // Add to container
      document.querySelector('.container').appendChild(heart);
      
      // Remove after animation
      setTimeout(() => {
        heart.remove();
      }, duration * 1000);
    }

    // Create hearts periodically
    function startHeartRain() {
      // Create initial hearts
      for(let i = 0; i < 10; i++) {
        setTimeout(createHeart, i * 300);
      }
      
      // Continue creating hearts
      setInterval(() => {
        if(document.querySelectorAll('.heart').length < 30) { // Limit max hearts
          createHeart();
        }
      }, 300);
    }

    // Start heart rain after a delay
    setTimeout(startHeartRain, 2000);
  </script>

  <script>

    // Audio control
    const bgMusic = document.getElementById('bgMusic');
    let hasInteracted = false;
    bgMusic.play();

    // Function to play audio
    function playAudio() {
      if (!hasInteracted) {
        bgMusic.play()
          .then(() => {
            hasInteracted = true;
          })
          .catch(e => console.log('Play failed:', e));
      }
    }

    // Play on any user interaction
    ['click', 'touchstart', 'keydown', 'scroll'].forEach(event => {
      document.addEventListener(event, playAudio, { once: true });
    });

    // Create a play button
    const playButton = document.createElement('button');
    playButton.innerHTML = '▶️ 播放音乐';
    playButton.style.cssText = `
      position: fixed;
      bottom: 20px;
      right: 20px;
      padding: 10px 20px;
      background: rgba(255, 255, 255, 0.8);
      border: none;
      border-radius: 20px;
      cursor: pointer;
      font-size: 16px;
      z-index: 100;
      transition: all 0.3s ease;
    `;

    playButton.addEventListener('mouseover', () => {
      playButton.style.background = 'rgba(255, 255, 255, 1)';
    });

    playButton.addEventListener('mouseout', () => {
      playButton.style.background = 'rgba(255, 255, 255, 0.8)';
    });

    playButton.addEventListener('click', () => {
      playAudio();
      playButton.style.display = 'none';
    });

    document.body.appendChild(playButton);

    // Hide button if audio starts playing
    bgMusic.addEventListener('play', () => {
      playButton.style.display = 'none';
    });
  </script>

</body>
 

</html> 

参考:小孙同学

部署

在NAS上部署docker:

version: '3.8'

services:
  python-linux:
    image: python:3.9-slim  # 使用官方 Python 镜像
    container_name: flower300day
    restart: unless-stopped
    volumes:
      - ./app-python39:/app  # 挂载本地目录到容器
    working_dir: /app  # 设置工作目录
    ports:
      - "8001:8000"  # 如果需要暴露端口
    environment:
      - PYTHONUNBUFFERED=1  # 禁用 Python 输出缓冲
    tty: true  # 保持容器运行
    stdin_open: true  # 允许交互式输入
    command: python3 -m http.server

用的和贪吃蛇同一个方式,只不过命令不同。

效果

访问网页,成功!一朵玫瑰缓缓渲染出来。

LICENSED UNDER CC BY-NC-SA 4.0