旗下品牌:
石家庄网站开发 石家庄网站开发公司

资讯动态

察而思、思而行、行而后语、知行合一

合影功能实现

发布时间:2025-08-15 热度:

设计一个具有合影功能的网页,允许用户拍照、合并多张照片并下载最终合影。

设计思路:

  • 使用摄像头捕获实时视频

  • 实现拍照功能并展示缩略图

  • 提供照片合并功能

  • 实现下载最终合影功能

  • 添加简洁美观的UI设计

实现方案:

  • 使用HTML5的getUserMedia API访问摄像头

  • 利用Canvas进行图像捕获和处理

  • 使用Flexbox布局创建响应式界面

下面是完整的实现代码:

<!DOCTYPE html> <html> <head>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=1.0">     <title>在线合影制作工具</title>     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">     <style>         * {             margin: 0;             padding: 0;             box-sizing: border-box;             font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;         }                  body {             background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);             min-height: 100vh;             display: flex;             justify-content: center;             align-items: center;             padding: 20px;             color: #333;         }                  .container {             width: 100%;             max-width: 1200px;             background: rgba(255, 255, 255, 0.9);             border-radius: 20px;             box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2);             overflow: hidden;             display: flex;             flex-direction: column;         }                  header {             background: linear-gradient(to right, #4a00e0, #8e2de2);             color: white;             padding: 20px;             text-align: center;         }                  header h1 {             font-size: 2.5rem;             margin-bottom: 10px;         }                  header p {             font-size: 1.1rem;             opacity: 0.9;         }                  .content {             display: flex;             flex-wrap: wrap;             padding: 20px;             gap: 20px;         }                  .camera-section {             flex: 1;             min-width: 300px;             background: white;             border-radius: 15px;             padding: 20px;             box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);         }                  .preview-section {             flex: 1;             min-width: 300px;             background: white;             border-radius: 15px;             padding: 20px;             box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);             display: flex;             flex-direction: column;         }                  .section-title {             font-size: 1.5rem;             margin-bottom: 15px;             color: #4a00e0;             display: flex;             align-items: center;             gap: 10px;         }                  .section-title i {             background: #4a00e0;             color: white;             width: 36px;             height: 36px;             border-radius: 50%;             display: flex;             align-items: center;             justify-content: center;         }                  .camera-container {             width: 100%;             height: 300px;             background: #f0f0f0;             border-radius: 10px;             overflow: hidden;             margin-bottom: 20px;             position: relative;             display: flex;             justify-content: center;             align-items: center;         }                  #video {             width: 100%;             height: 100%;             object-fit: cover;         }                  .controls {             display: flex;             gap: 10px;             flex-wrap: wrap;         }                  .btn {             padding: 12px 24px;             border: none;             border-radius: 50px;             font-size: 1rem;             font-weight: 600;             cursor: pointer;             display: flex;             align-items: center;             gap: 8px;             transition: all 0.3s ease;         }                  .btn-primary {             background: linear-gradient(to right, #4a00e0, #8e2de2);             color: white;             box-shadow: 0 4px 10px rgba(74, 0, 224, 0.3);         }                  .btn-primary:hover {             transform: translateY(-3px);             box-shadow: 0 6px 15px rgba(74, 0, 224, 0.4);         }                  .btn-success {             background: linear-gradient(to right, #00b09b, #96c93d);             color: white;             box-shadow: 0 4px 10px rgba(0, 176, 155, 0.3);         }                  .btn-success:hover {             transform: translateY(-3px);             box-shadow: 0 6px 15px rgba(0, 176, 155, 0.4);         }                  .btn-danger {             background: linear-gradient(to right, #ff416c, #ff4b2b);             color: white;             box-shadow: 0 4px 10px rgba(255, 65, 108, 0.3);         }                  .btn-danger:hover {             transform: translateY(-3px);             box-shadow: 0 6px 15px rgba(255, 65, 108, 0.4);         }                  .captured-photos {             display: flex;             gap: 15px;             margin: 20px 0;             flex-wrap: wrap;             min-height: 120px;         }                  .photo-thumb {             width: 100px;             height: 100px;             border-radius: 10px;             overflow: hidden;             position: relative;             box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);             border: 3px solid #8e2de2;         }                  .photo-thumb img {             width: 100%;             height: 100%;             object-fit: cover;         }                  .photo-thumb .remove {             position: absolute;             top: 5px;             right: 5px;             background: rgba(255, 255, 255, 0.8);             border-radius: 50%;             width: 22px;             height: 22px;             display: flex;             align-items: center;             justify-content: center;             cursor: pointer;             color: #ff416c;             font-size: 14px;         }                  .result-container {             flex: 1;             background: #f8f9fa;             border-radius: 10px;             display: flex;             justify-content: center;             align-items: center;             margin-top: 20px;             overflow: hidden;             min-height: 300px;         }                  #result {             max-width: 100%;             max-height: 100%;             display: none;         }                  .placeholder {             color: #6c757d;             text-align: center;             padding: 20px;         }                  .placeholder i {             font-size: 3rem;             margin-bottom: 15px;             color: #adb5bd;         }                  .download-btn {             margin-top: 20px;             align-self: center;             padding: 12px 30px;             font-size: 1.1rem;         }                  .instructions {             background: #eef2ff;             border-left: 4px solid #4a00e0;             padding: 15px;             border-radius: 0 8px 8px 0;             margin: 20px 0;         }                  .instructions h3 {             margin-bottom: 10px;             color: #4a00e0;         }                  .instructions ol {             padding-left: 20px;         }                  .instructions li {             margin-bottom: 8px;         }                  footer {             text-align: center;             padding: 20px;             background: #f8f9fa;             color: #6c757d;             font-size: 0.9rem;         }                  @media (max-width: 768px) {             .content {                 flex-direction: column;             }                          header h1 {                 font-size: 2rem;             }         }                  .flash {             position: absolute;             top: 0;             left: 0;             width: 100%;             height: 100%;             background-color: white;             opacity: 0;             pointer-events: none;         }                  @keyframes flash {             0% { opacity: 0; }             50% { opacity: 0.8; }             100% { opacity: 0; }         }     </style> </head> <body>     <div>         <header>             <h1><i class="fas fa-camera"></i> 在线合影制作工具</h1>             <p>拍摄照片,合并多张图片,创建完美的合影</p>         </header>                  <div>             <div>                 <h2><i class="fas fa-video"></i> 拍照区域</h2>                                  <div>                     <div id="flash"></div>                     <video id="video" autoplay playsinline></video>                     <div id="camera-placeholder">                         <p>正在请求摄像头权限...</p>                     </div>                 </div>                                  <div>                     <button id="capture-btn" class="btn btn-primary">                         <i class="fas fa-camera"></i> 拍照                     </button>                     <button id="reset-btn" class="btn btn-danger">                         <i class="fas fa-trash-alt"></i> 重置所有照片                     </button>                 </div>                                  <div>                     <h3>使用说明:</h3>                     <ol>                         <li>点击"允许"按钮授权访问您的摄像头</li>                         <li>调整位置,点击"拍照"按钮拍摄照片</li>                         <li>拍摄多张照片后,点击"合并照片"创建合影</li>                         <li>最后点击"下载合影"保存最终图片</li>                     </ol>                 </div>                                  <h3><i class="fas fa-images"></i> 已拍摄照片</h3>                 <div id="photos-container">                     <div>                         <i class="fas fa-image"></i>                         <p>尚未拍摄照片</p>                     </div>                 </div>                                  <button id="merge-btn" class="btn btn-success">                     <i class="fas fa-object-group"></i> 合并照片                 </button>             </div>                          <div>                 <h2><i class="fas fa-file-image"></i> 合影预览</h2>                                  <div>                     <div id="result-placeholder">                         <i class="fas fa-users"></i>                         <p>合并后的合影将显示在这里</p>                     </div>                     <img id="result" alt="合并后的合影">                 </div>                                  <button id="download-btn" class="btn btn-primary download-btn">                     <i class="fas fa-download"></i> 下载合影                 </button>             </div>         </div>                  <footer>             <p>© 2023 在线合影工具 | 使用HTML5技术实现 | 兼容Chrome、Firefox、Edge等现代浏览器</p>         </footer>     </div>     <script>         document.addEventListener('DOMContentLoaded', function() {             // 获取DOM元素             const video = document.getElementById('video');             const captureBtn = document.getElementById('capture-btn');             const resetBtn = document.getElementById('reset-btn');             const mergeBtn = document.getElementById('merge-btn');             const downloadBtn = document.getElementById('download-btn');             const photosContainer = document.getElementById('photos-container');             const resultImg = document.getElementById('result');             const resultPlaceholder = document.getElementById('result-placeholder');             const flash = document.getElementById('flash');             const cameraPlaceholder = document.getElementById('camera-placeholder');                          // 存储拍摄的照片             let capturedPhotos = [];                          // 访问摄像头             async function initCamera() {                 try {                     const stream = await navigator.mediaDevices.getUserMedia({                          video: {                              width: { ideal: 1280 },                              height: { ideal: 720 },                             facingMode: 'user'                          }                      });                     video.srcObject = stream;                     cameraPlaceholder.style.display = 'none';                 } catch (err) {                     console.error("摄像头访问错误:", err);                     cameraPlaceholder.innerHTML = `<p style="color:red;">摄像头访问失败: ${err.message}</p>`;                 }             }                          // 拍照功能             function capturePhoto() {                 const canvas = document.createElement('canvas');                 canvas.width = video.videoWidth;                 canvas.height = video.videoHeight;                                  const ctx = canvas.getContext('2d');                 ctx.drawImage(video, 0, 0, canvas.width, canvas.height);                                  // 添加闪光效果                 flash.style.animation = 'flash 0.5s';                 setTimeout(() => {                     flash.style.animation = '';                 }, 500);                                  // 生成数据URL                 const dataURL = canvas.toDataURL('image/png');                                  // 添加到照片数组                 capturedPhotos.push(dataURL);                                  // 更新照片缩略图                 updatePhotoThumbnails();             }                          // 更新照片缩略图             function updatePhotoThumbnails() {                 if (capturedPhotos.length === 0) {                     photosContainer.innerHTML = `                         <div>                             <i class="fas fa-image"></i>                             <p>尚未拍摄照片</p>                         </div>                     `;                     return;                 }                                  photosContainer.innerHTML = '';                                  capturedPhotos.forEach((photo, index) => {                     const thumbDiv = document.createElement('div');                     thumbDiv.className = 'photo-thumb';                                          const img = document.createElement('img');                     img.src = photo;                     img.alt = `照片 ${index + 1}`;                                          const removeBtn = document.createElement('div');                     removeBtn.className = 'remove';                     removeBtn.innerHTML = '<i class="fas fa-times"></i>';                     removeBtn.onclick = () => removePhoto(index);                                          thumbDiv.appendChild(img);                     thumbDiv.appendChild(removeBtn);                     photosContainer.appendChild(thumbDiv);                 });             }                          // 移除照片             function removePhoto(index) {                 capturedPhotos.splice(index, 1);                 updatePhotoThumbnails();             }                          // 重置所有照片             function resetPhotos() {                 if (capturedPhotos.length === 0) return;                                  if (confirm('确定要删除所有照片吗?')) {                     capturedPhotos = [];                     updatePhotoThumbnails();                     resultImg.style.display = 'none';                     resultPlaceholder.style.display = 'flex';                 }             }                          // 合并照片             function mergePhotos() {                 if (capturedPhotos.length === 0) {                     alert('请先拍摄至少一张照片!');                     return;                 }                                  // 计算画布尺寸                 const maxWidth = 800;                 const thumbWidth = Math.floor(maxWidth / Math.min(capturedPhotos.length, 4));                 const thumbHeight = Math.floor(thumbWidth * 0.75);                 const rows = Math.ceil(capturedPhotos.length / 4);                                  const canvas = document.createElement('canvas');                 canvas.width = Math.min(capturedPhotos.length, 4) * thumbWidth;                 canvas.height = rows * thumbHeight;                                  const ctx = canvas.getContext('2d');                 ctx.fillStyle = '#ffffff';                 ctx.fillRect(0, 0, canvas.width, canvas.height);                                  // 绘制所有照片                 let col = 0, row = 0;                                  for (let i = 0; i < capturedPhotos.length; i++) {                     const img = new Image();                     img.src = capturedPhotos[i];                                          ctx.drawImage(                         img,                          col * thumbWidth,                          row * thumbHeight,                          thumbWidth,                          thumbHeight                     );                                          // 绘制照片边框                     ctx.strokeStyle = 'rgba(142, 45, 226, 0.5)';                     ctx.lineWidth = 2;                     ctx.strokeRect(                         col * thumbWidth,                          row * thumbHeight,                          thumbWidth,                          thumbHeight                     );                                          col++;                     if (col >= 4) {                         col = 0;                         row++;                     }                 }                                  // 添加标题                 ctx.fillStyle = '#4a00e0';                 ctx.font = 'bold 24px Arial';                 ctx.textAlign = 'center';                 ctx.fillText('美好回忆', canvas.width / 2, 30);                                  // 添加日期                 const now = new Date();                 const dateStr = now.toLocaleDateString();                 ctx.fillStyle = '#6c757d';                 ctx.font = '16px Arial';                 ctx.fillText(dateStr, canvas.width / 2, canvas.height - 20);                                  // 显示结果                 resultImg.src = canvas.toDataURL('image/png');                 resultImg.style.display = 'block';                 resultPlaceholder.style.display = 'none';             }                          // 下载合影             function downloadResult() {                 if (!resultImg.src || resultImg.style.display === 'none') {                     alert('请先合并照片!');                     return;                 }                                  const link = document.createElement('a');                 link.download = `合影-${new Date().getTime()}.png`;                 link.href = resultImg.src;                 link.click();             }                          // 事件监听             captureBtn.addEventListener('click', capturePhoto);             resetBtn.addEventListener('click', resetPhotos);             mergeBtn.addEventListener('click', mergePhotos);             downloadBtn.addEventListener('click', downloadResult);                          // 初始化摄像头             initCamera();                          // 初始更新缩略图             updatePhotoThumbnails();         });     </script> </body> </html>

功能说明

这个合影工具具有以下功能:

  1. 摄像头访问:请求用户摄像头权限并显示实时视频

  2. 拍照功能:点击"拍照"按钮可捕获当前视频帧为照片

  3. 照片管理:

    1. 显示所有拍摄照片的缩略图

    2. 可删除单张照片

    3. 可重置所有照片

  4. 合影制作:

    1. 将多张照片合并为一张合影

    2. 自动排列照片(每行最多4张)

    3. 添加标题和日期水印

  5. 下载功能:将合并后的合影下载为PNG图片

使用说明

  1. 打开页面后允许浏览器访问您的摄像头

  2. 调整位置,点击"拍照"按钮拍摄照片

  3. 可拍摄多张照片(显示在下方区域)

  4. 点击"合并照片"按钮创建合影

  5. 点击"下载合影"保存最终图片

这个应用使用了现代Web API(getUserMedia, Canvas)实现,不需要任何外部库,在大多数现代浏览器中都能正常工作。

注意:由于安全限制,该页面需要在HTTPS环境或localhost下运行才能访问摄像头。

 

 

 

 

 

 

联系尚武科技
客户服务
石家庄APP开发
400-666-4864
为您提供售前购买咨询、解决方案推荐等1V1服务!
技术支持及售后
石家庄APP开发公司
0311-66682288
为您提供从产品到服务的全面技术支持 !
客户服务
石家庄小程序开发
石家庄小程序开发公司
加我企业微信
为您提供售前购买咨询、
解决方案推荐等1V1服务!
石家庄网站建设公司
咨询相关问题或预约面谈,可以通过以下方式与我们联系。
石家庄网站制作
在线联系:
石家庄Web开发
石家庄软件开发
石家庄软件开发公司
ADD/地址:
河北·石家庄
新华区西三庄大街86号河北互联网大厦B座二层
Copyright © 2008-2025尚武科技 保留所有权利。 冀ICP备12011207号-2 石家庄网站开发冀公网安备 13010502001294号《互联网平台公约协议》
Copyright © 2025 www.sw-tech.cn, Inc. All rights reserved