Просмотр исходного кода

/share分享页面增加填色动画

guoziyun 11 месяцев назад
Родитель
Сommit
9e9016e981

+ 1 - 0
app.js

@@ -138,6 +138,7 @@ app.use('/download', require('./routes/res/download'));
 app.use('/api/comment', require('./routes/v2/comment'));  // 评论
 app.use('/api/contact', require('./routes/v2/contact'));  // 联系信息
 app.use('/api/subscribe', require('./routes/v2/subscribe'));  // 用户提交邮箱订阅
+app.use('/api/tasks', require('./routes/v2/tasks'));  // 用户分享前提交task列表
 
 
 //v1

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/WorkLayer-CuLsTjxY.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/main-BVJfb_x5.js


Разница между файлами не показана из-за своего большого размера
+ 30 - 0
dist/assets/main-DOADjK2a.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/share-BWPVpJpF.js


Разница между файлами не показана из-за своего большого размера
+ 39 - 0
dist/index.html


+ 185 - 0
dist/share.html

@@ -0,0 +1,185 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta http-equiv="x-ua-compatible" content="ie=edge">
+  <title>Art Number Coloring</title>
+
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <meta name="description" content="Free Coloring Pages Paint Online!">
+  <meta name="keywords" content="color, paint, app, ios, android, game">
+
+  <meta property="og:site_name" content="art.pcoloring.com">
+  <meta property="og:title" content="Art Number Coloring">
+  <meta property="og:description" content="Free Coloring Pages Paint Online!">
+  <meta property="og:image" content="https://d2mb6s2cy1zg97.cloudfront.net/thumbs/coloring-page/work/480/686240183deb2d264e3a39d9.webp">
+  <meta property="og:type" content="website">
+
+  <!-- MARK: Universal Link / Android App Link 的核心配置 -->
+  <!-- 这些 meta 标签的值应该是完整的 HTTPS 链接,Facebook 会识别并尝试拉起 App -->
+  <meta property="og:url" content="https://art.pcoloring.com/share/686240183deb2d264e3a39d9" />
+  <!-- **Universal Link 路径** -->
+  <meta property="al:ios:url" content="https://art.pcoloring.com/share/686240183deb2d264e3a39d9" />
+  <!-- **Universal Link 路径** -->
+  <meta property="al:ios:app_store_id" content="1575480118" /> <!-- **iOS App Store ID** -->
+  <meta property="al:ios:app_name" content="Art Number Coloring Book" /> <!-- **iOS 应用名称** -->
+
+  <meta property="al:android:package" content="com.pcoloring.art.puzzle.color.by.number" /> <!-- **Android 包名** -->
+  <meta property="al:android:url" content="https://art.pcoloring.com/share/686240183deb2d264e3a39d9" />
+  <!-- ** Universal Link 路径** -->
+  <meta property="al:android:app_name" content="Art Number Coloring Book" /> <!-- **Android 应用名称** -->
+
+  <meta name="apple-itunes-app" content="app-id=1575480118">
+
+  <link rel="icon" href="/assets/icon/favicon.ico" type="image/x-icon">
+  <link rel="apple-touch-icon" sizes="180x180" href="/assets/icon/icon.png">
+
+  <style>
+    :root {
+      --primary-color: #ff6b6b;
+      --secondary-color: #4ecdc4;
+      --accent-color: #ffd166;
+      --background-color: #f9f9f9;
+      --text-color: #333;
+      --light-text: #666;
+      --border-color: #e0e0e0;
+    }
+    
+    body {
+      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+      display: flex;
+      flex-direction: column; /* 垂直方向排列子元素 */
+      justify-content: center; /* 垂直居中 */
+      align-items: center;     /* 水平居中 */
+      min-height: 100vh;       /* 最小高度为视口高度,确保垂直居中 */
+      margin: 0;
+      padding: 20px;
+      box-sizing: border-box; /* 盒模型为边框盒 */
+      background-color: var(--background-color);
+      color: var(--text-color);
+      line-height: 1.6;
+
+    }
+
+    /* 父容器,用于包裹图片和 Canvas,并使其相对定位 */
+    .image-canvas-container {
+      position: relative;
+      max-width: 90%; /* 限制容器最大宽度 */
+      height: auto;
+      display: inline-block; /* 确保容器根据图片尺寸收缩 */
+      box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); /* 添加阴影效果 */
+      border-radius: 12px; /* 圆角 */
+      overflow: hidden; /* 隐藏超出容器的内容 */
+    }
+
+    /* 图片样式:使其响应式并填充容器 */
+    .image-canvas-container img {
+      position: relative;
+      display: block; /* 移除图片底部空白 */
+      max-width: 100%; /* 最大宽度为父容器的100% */
+      height: auto;    /* 高度自动调整,保持图片比例 */
+      border-radius: 12px; /* 与容器相同的圆角 */
+      z-index: 100;
+    }
+
+    /* Canvas 样式:绝对定位,与图片完全重叠 */
+    .image-canvas-container canvas {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      border-radius: 12px; /* 与容器相同的圆角 */
+      z-index: 50;
+    }
+
+    .btn {
+      display: inline-block;
+      background-color: var(--primary-color);
+      color: white;
+      border: none;
+      padding: 10px 20px;
+      border-radius: 5px;
+      font-size: 1.1rem;
+      font-weight: 500;
+      cursor: pointer;
+      transition: background-color 0.3s ease;
+      text-decoration: none;
+      text-align: center;
+      margin-right: 10px;
+      margin-bottom: 10px;
+    }
+
+    .btn:hover {
+      background-color: #ff4d4d;
+    }
+
+    .btn-secondary {
+      background-color: var(--secondary-color);
+    }
+
+    .btn-secondary:hover {
+      background-color: #37b0a8;
+    }
+
+    .play-button {
+      position: absolute;
+      display: block;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      width: 80px;
+      height: 80px;
+      background-color: rgba(255, 107, 107, 0.8);
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      transition: all 0.3s ease;
+      z-index: 0;
+    }
+
+    .play-button:hover {
+      background-color: rgba(255, 107, 107, 1);
+      transform: translate(-50%, -50%) scale(1.1);
+    }
+
+    .play-button::after {
+      content: "";
+      width: 0;
+      height: 0;
+      border-top: 15px solid transparent;
+      border-bottom: 15px solid transparent;
+      border-left: 25px solid white;
+      margin-left: 5px;
+    }
+
+    .buttons {
+      width: 100%;
+      max-width: 550px;
+      display: flex;
+      flex-wrap: wrap;
+      justify-content: space-between;
+      align-items: center;
+      margin-top: 40px;
+    }
+  </style>
+  <script type="module" crossorigin src="/assets/share-BWPVpJpF.js"></script>
+  <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
+  <link rel="modulepreload" crossorigin href="/assets/WorkLayer-CuLsTjxY.js">
+</head>
+
+<body>
+  <div class="image-canvas-container">
+    <img id="poster-img" src="https://d2mb6s2cy1zg97.cloudfront.net/thumbs/coloring-page/work/480/686240183deb2d264e3a39d9.webp" alt="Art Number Coloring" />
+    <canvas id="canvas"></canvas>
+    <div id="play-button" class="play-button"></div>
+  </div>
+  <div class="buttons">
+    <a href="/">Home</a>
+    <a href="#>" class="btn btn-secondary">Download App</a>
+    <a href="#">Detail>></a>
+  </div>
+</body>
+
+</html>

+ 2 - 1
models/index.js

@@ -16,4 +16,5 @@ module.exports.TagSeo = mongoose.model('TagSeo', require('./schema-tag-seo'));
 
 module.exports.Comment = mongoose.model('Comment', require('./schema-comment'));
 module.exports.Contact = mongoose.model('Contact', require('./schema-contact'));
-module.exports.Subscribe = mongoose.model('Subscribe', require('./schema-subscribe'));
+module.exports.Subscribe = mongoose.model('Subscribe', require('./schema-subscribe'));
+module.exports.Task = mongoose.model('Task', require('./schema-task'));

+ 29 - 0
models/schema-task.js

@@ -0,0 +1,29 @@
+var Schema = require('mongoose').Schema;
+
+const taskSchema = new Schema({
+  uuid: {
+    type: String,
+    required: true,
+    index: true // 为 uuid 字段创建索引以提高查询性能
+  },
+  art: {
+    type: String,
+    required: true,
+    index: true // 为 art 字段创建索引以提高查询性能
+  },
+  tasks: { // 存储 number 数组
+    type: [Number], // 定义为数字数组
+    default: [] // 默认值为空数组
+  },
+  createdAt: {
+    type: Date,
+    default: Date.now
+  }
+});
+
+// 为 uuid 和 art 字段创建复合唯一索引
+// 这确保了 uuid 和 art 的组合是唯一的,如果尝试插入重复的组合会报错
+taskSchema.index({ uuid: 1, art: 1 }, { unique: true });
+
+
+module.exports = taskSchema;

+ 77 - 0
routes/v2/tasks.js

@@ -0,0 +1,77 @@
+var express = require('express');
+var createError = require('http-errors')
+var router = express.Router();
+
+const models = require('../../models');
+
+
+// 获取task
+router.get('/', function (req, res, next) {
+  (async function () {
+    const { uuid, art } = req.query; // 从查询参数中获取 uuid 和 art
+    // 1. 参数校验
+    if (!uuid || !art) {
+      return res.status(400).json({ message: '请求参数缺失。uuid 和 art 均为必填项。' });
+    }
+    // 2. 根据 uuid 和 art 查找记录
+    const taskRecord = await models.Task.findOne({ uuid: uuid, art: art });
+
+    // 3. 判断是否找到记录
+    if (!taskRecord) {
+      return res.status(404).json({ message: '未找到匹配的任务数据。' });
+    }
+
+    // 4. 返回找到的任务数据
+    res.status(200).json(taskRecord.tasks);
+  })().catch(next);
+
+});
+
+
+// 提交task id列表
+router.post('/', async (req, res) => {
+  try {
+    // 验证输入
+    const { uuid, art, tasks } = req.body; // 从请求体中获取参数
+
+    // 1. 参数校验
+    if (!uuid || !art || !Array.isArray(tasks)) {
+      return res.status(400).json({ message: '请求参数缺失或格式不正确。uuid, art 和 tasks (number数组) 均为必填项。' });
+    }
+
+    // 2. 验证 tasks 数组中的元素是否都是数字
+    if (tasks.some(isNaN)) {
+      return res.status(400).json({ message: 'tasks 数组中包含非数字元素。' });
+    }
+
+    // 3. 使用 findOneAndUpdate 进行 upsert 操作
+    // query: 根据 uuid 和 art 查找记录
+    const query = { uuid: uuid, art: art };
+    // update: 设置或更新 tasks 字段,并更新 updatedAt 时间
+    const update = { tasks: tasks, updatedAt: Date.now() };
+    // options:
+    //   upsert: true - 如果找不到匹配的记录,则创建一条新记录
+    //   new: true - 返回更新后的文档,而不是原始文档
+    const options = { upsert: true, new: true, setDefaultsOnInsert: true };
+
+    const taskRecord = await models.Task.findOneAndUpdate(query, update, options);
+
+    // 4. 返回成功响应
+    res.status(200).json({
+      message: '任务数据提交成功!',
+      data: taskRecord
+    });
+
+  } catch (error) {
+    console.error('提交任务数据时发生错误:', error);
+    // 5. 错误处理
+    if (error.code === 11000) { // MongoDB 唯一索引冲突错误码
+      return res.status(409).json({ message: '数据冲突:uuid 和 art 的组合已存在,但更新失败。' });
+    }
+    res.status(500).json({ message: '服务器内部错误,提交失败。' });
+  }
+});
+
+
+
+module.exports = router;

+ 3 - 2
views/v2/play.ejs

@@ -28,10 +28,11 @@
   <meta property="al:android:app_name" content="Art Number Coloring Book" /> <!-- **Android 应用名称** -->
 
 
-  <script type="module" crossorigin src="/assets/main-BVJfb_x5.js"></script>
+  <script type="module" crossorigin src="/assets/main-DOADjK2a.js"></script>
   <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
+  <link rel="modulepreload" crossorigin href="/assets/WorkLayer-CuLsTjxY.js">
   <link rel="stylesheet" crossorigin href="/assets/main-BVTiTYwv.css">
-
+  
   <script type="text/javascript"
     src="https://platform-api.sharethis.com/js/sharethis.js#property=67e0d66a54a3d000192a4615&product=inline-share-buttons&source=platform"
     async="async"></script>

+ 74 - 26
views/v2/share.ejs

@@ -4,7 +4,6 @@
 <head>
   <meta http-equiv="x-ua-compatible" content="ie=edge">
   <title>Art Number Coloring</title>
-  <link rel='stylesheet' href='/stylesheets/share.css' />
 
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <meta name="description" content="Free Coloring Pages Paint Online!">
@@ -43,23 +42,52 @@
       --border-color: #e0e0e0;
     }
 
-    * {
-      margin: 0;
-      padding: 0;
-      box-sizing: border-box;
-      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
-    }
-
     body {
+      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+      display: flex;
+      flex-direction: column; /* 垂直方向排列子元素 */
+      justify-content: center; /* 垂直居中 */
+      align-items: center;     /* 水平居中 */
+      min-height: 100vh;       /* 最小高度为视口高度,确保垂直居中 */
+      margin: 0;
+      padding: 20px;
+      box-sizing: border-box; /* 盒模型为边框盒 */
       background-color: var(--background-color);
       color: var(--text-color);
       line-height: 1.6;
+
+    }
+
+    /* 父容器,用于包裹图片和 Canvas,并使其相对定位 */
+    .image-canvas-container {
+      position: relative;
+      max-width: 90%; /* 限制容器最大宽度 */
+      height: auto;
+      display: inline-block; /* 确保容器根据图片尺寸收缩 */
+      box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); /* 添加阴影效果 */
+      border-radius: 12px; /* 圆角 */
+      overflow: hidden; /* 隐藏超出容器的内容 */
+    }
+
+    /* 图片样式:使其响应式并填充容器 */
+    .image-canvas-container img {
+      position: relative;
+      display: block; /* 移除图片底部空白 */
+      max-width: 100%; /* 最大宽度为父容器的100% */
+      height: auto;    /* 高度自动调整,保持图片比例 */
+      border-radius: 12px; /* 与容器相同的圆角 */
+      z-index: 100;
     }
 
-    .container {
-      max-width: 1200px;
-      margin: 0 auto;
-      padding: 0 20px;
+    /* Canvas 样式:绝对定位,与图片完全重叠 */
+    .image-canvas-container canvas {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      border-radius: 12px; /* 与容器相同的圆角 */
+      z-index: 50;
     }
 
     .btn {
@@ -91,22 +119,36 @@
       background-color: #37b0a8;
     }
 
-    .content {
-      height: 100%;
-      width: 100%;
-      position: relative;
-      overflow: hidden;
+    .play-button {
+      position: absolute;
+      display: block;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      width: 80px;
+      height: 80px;
+      background-color: rgba(255, 107, 107, 0.8);
+      border-radius: 50%;
       display: flex;
-      flex-direction: column;
       align-items: center;
       justify-content: center;
+      transition: all 0.3s ease;
+      z-index: 0;
     }
 
-    .content img {
-      width: 100%;
-      max-width: 550px;
-      height: auto;
-      margin: 20px;
+    .play-button:hover {
+      background-color: rgba(255, 107, 107, 1);
+      transform: translate(-50%, -50%) scale(1.1);
+    }
+
+    .play-button::after {
+      content: "";
+      width: 0;
+      height: 0;
+      border-top: 15px solid transparent;
+      border-bottom: 15px solid transparent;
+      border-left: 25px solid white;
+      margin-left: 5px;
     }
 
     .buttons {
@@ -116,14 +158,20 @@
       flex-wrap: wrap;
       justify-content: space-between;
       align-items: center;
+      margin-top: 40px;
     }
   </style>
+  <script type="module" crossorigin src="/assets/share-BWPVpJpF.js"></script>
+  <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
+  <link rel="modulepreload" crossorigin href="/assets/WorkLayer-CuLsTjxY.js">
 </head>
 
 <body>
-  <div class="container">
-    <div class="content">
-      <img src="<%= imageUrl %>" alt="Art Number Coloring" />
+  <div class="image-canvas-container">
+    <img id="poster-img" src="<%= imageUrl %>" alt="Art Number Coloring" />
+    <canvas id="canvas"></canvas>
+    <div id="play-button" class="play-button"></div>
+  </div>
       <div class="buttons">
         <a href="/">Home</a>
         <a href="<%= downlink %>" class="btn btn-secondary">Download App</a>

Некоторые файлы не были показаны из-за большого количества измененных файлов