首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《Nuxt.js 实战:从放弃到入门》四、轻松制作朋友圈九宫格图片

《Nuxt.js 实战:从放弃到入门》四、轻松制作朋友圈九宫格图片

作者头像
小码农薛尧
发布2025-02-21 12:56:55
发布2025-02-21 12:56:55
2120
举报
文章被收录于专栏:小码农薛尧小码农薛尧
20250215211739371684.png
20250215211739371684.png

功能说明

  1. 设计风格:图片分割功能页面将参考某网页的设计风格,使用 Element Plus 组件库实现界面,旨在确保用户体验流畅,同时保证界面的美观性与易用性。
  2. 页面功能
  • 图片上传区域:支持用户将图片直接拖拽到指定区域进行上传,也可点击上传。仅支持 JPG 和 PNG 格式,文件大小限制为 10MB。
  • 分割参数设置:用户可通过输入行数和列数来设置图片分割的参数,行数和列数的取值范围为 1 到 10。
  • 预览区域:提供上传图片的预览以及分割后图片的预览,方便用户查看分割效果。
  • 下载功能:支持将分割后的图片打包成 ZIP 文件进行下载。

代码实现

  1. 创建页面文件:在项目中创建divide.vue文件,用于实现图片分割功能页面。
代码语言:javascript
复制

<template>
  <div class="container">
    <el-card>
      <template #header>
        <div class="text-center">
          <span class="text-xl font-medium">图片分割</span>
        </div>
      </template>

      <!-- 图片上传区域 -->
      <el-upload
          class="upload-area"
          drag
          :auto-upload="false"
          :show-file-list="false"
          accept="image/*"
          :on-change="handleFileSelect"
      >
        <template #trigger>
          <div v-if="!imageUrl && !isLoading && !errorMessage" class="text-center">
            <el-icon class="el-icon--upload mx-auto block"><upload-filled /></el-icon>
            <div class="el-upload__text">
              拖拽图片到此处或 <em>点击上传</em>
            </div>
            <div class="text-sm text-gray-400 mt-2">支持 JPG、PNG 格式</div>
          </div>
        </template>
      </el-upload>

      <div v-if="isLoading" class="text-center">
        <el-icon class="is-loading" :size="24"><loading /></el-icon>
        <div class="text-gray-600">正在处理图片...</div>
      </div>

      <div v-else-if="errorMessage" class="text-center">
        <el-alert
            :title="errorMessage"
            type="error"
            :closable="false"
        />
        <el-button type="primary" @click="errorMessage = ''">
          重试
        </el-button>
      </div>

      <div v-else-if="imageUrl" class="image-container">
        <!-- 图片预览 -->
        <el-image
            :src="imageUrl"
            fit="contain"
            class="preview-image"
        />

        <!-- 分割参数设置 -->
        <el-form class="mt-4">
          <el-form-item label="分割设置">
            <el-row :gutter="20">
              <el-col :span="12">
                <el-input-number
                    v-model="rows"
                    :min="1"
                    :max="10"
                    label="行数"
                    placeholder="行数"
                />
              </el-col>
              <el-col :span="12">
                <el-input-number
                    v-model="cols"
                    :min="1"
                    :max="10"
                    label="列数"
                    placeholder="列数"
                />
              </el-col>
            </el-row>
          </el-form-item>

          <el-button
              type="primary"
              @click="divideImage"
              :loading="isProcessing"
          >
            开始分割
          </el-button>
        </el-form>

        <!-- 分割结果预览 -->
        <div v-if="dividedImages.length > 0" class="divided-images mt-4">
          <h3 class="text-lg font-medium mb-2">分割结果预览</h3>
          <div class="grid-container" :style="gridStyle">
            <div v-for="(image, index) in dividedImages" :key="index" class="grid-item">
              <el-image
                  :src="image"
                  fit="contain"
                  class="divided-image"
              />
            </div>
          </div>
          <div class="mt-4 text-right">
            <el-button type="primary" @click="downloadAll">
              下载全部
            </el-button>
          </div>
        </div>
      </div>
    </el-card>
</div>
</template>

<script setup>
import { ref, computed } from'vue'
import { UploadFilled, Loading } from'@element-plus/icons-vue'

// 状态变量
const imageUrl = ref('')
const originalImage = ref(null)
const rows = ref(2)
const cols = ref(2)
const isLoading = ref(false)
const isProcessing = ref(false)
const errorMessage = ref('')
const dividedImages = ref([])

// 文件大小限制(10MB)
const MAX_FILE_SIZE = 10 * 1024 * 1024
// 支持的图片格式
const SUPPORTED_FORMATS = ['image/jpeg', 'image/png']

// 计算分割预览网格样式
const gridStyle = computed(() => ({
display: 'grid',
gridTemplateColumns: `repeat(${cols.value}, 1fr)`,
gap: '10px',
marginTop: '10px'
}))

// 处理文件选择
const handleFileSelect = async (uploadFile) => {
const file = uploadFile.raw
if (!validateFile(file)) return

  isLoading.value = true
  errorMessage.value = ''

try {
    const reader = new FileReader()
    reader.onload = (e) => {
      const img = new Image()
      img.onload = () => {
        imageUrl.value = e.target.result
        originalImage.value = img
        isLoading.value = false
      }
      img.onerror = () => {
        errorMessage.value = '图片加载失败'
        isLoading.value = false
      }
      img.src = e.target.result
    }
    reader.readAsDataURL(file)
  } catch (error) {
    errorMessage.value = '图片处理失败'
    isLoading.value = false
  }
}

// 验证文件
const validateFile = (file) => {
if (!SUPPORTED_FORMATS.includes(file.type)) {
    ElMessage.error('请上传 JPG 或 PNG 格式的图片')
    returnfalse
  }
if (file.size > MAX_FILE_SIZE) {
    ElMessage.error('图片大小不能超过 10MB')
    returnfalse
  }
returntrue
}

// 分割图片
const divideImage = async () => {
if (!originalImage.value) return

  isProcessing.value = true
  dividedImages.value = []

try {
    const canvas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')
    const img = originalImage.value

    const segmentWidth = img.width / cols.value
    const segmentHeight = img.height / rows.value

    for (let i = 0; i < rows.value; i++) {
      for (let j = 0; j < cols.value; j++) {
        canvas.width = segmentWidth
        canvas.height = segmentHeight

        // 清除画布
        ctx.clearRect(0, 0, canvas.width, canvas.height)

        // 绘制分割后的图片部分
        ctx.drawImage(
            img,
            j * segmentWidth,
            i * segmentHeight,
            segmentWidth,
            segmentHeight,
            0,
            0,
            segmentWidth,
            segmentHeight
        )

        // 将分割后的图片添加到数组
        dividedImages.value.push(canvas.toDataURL('image/png'))
      }
    }
  } catch (error) {
    ElMessage.error('图片分割失败')
  } finally {
    isProcessing.value = false
  }
}

// 下载所有分割后的图片
const downloadAll = () => {
  dividedImages.value.forEach((dataUrl, index) => {
    const link = document.createElement('a')
    link.href = dataUrl
    link.download = `divided-image-${index + 1}.png`
    link.click()
  })
}
</script>

<style scoped>
.image-container {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
}

.preview-image {
max-width: 100%;
max-height: 400px;
margin-bottom: 20px;
}

.divided-images {
width: 100%;
}

.grid-item {
border: 1px solid #eee;
padding: 5px;
background: #fff;
}

.divided-image {
width: 100%;
height: 100%;
object-fit: contain;
}
</style>
  1. 第三方库集成:为实现下载时将分割后的图片打包成 ZIP 文件,需要安装jszip库。使用以下命令进行安装:
代码语言:javascript
复制

npm install jszip

在代码中引入jszip库,并修改downloadAll函数以实现 ZIP 文件的创建和下载:

代码语言:javascript
复制

import JSZipfrom'jszip'

//下载所有分割后的图片
constdownloadAll=async()=>{
constzip=newJSZip()
constfolder=zip.folder('divided-images')

dividedImages.value.forEach((dataUrl,index)=>{
    constbase64Data=dataUrl.replace(/^data:image\/(png|jpg);base64,/,'')
    folder.file(`divided-image-${index+1}.png`,base64Data,{base64:true})
})

try{
    constcontent=awaitzip.generateAsync({type:'blob'})
    constlink=document.createElement('a')
    link.href=URL.createObjectURL(content)
    link.download='divided-images.zip'
    link.click()
    URL.revokeObjectURL(link.href)
}catch(error){
    ElMessage.error('打包下载失败')
}
}
  1. 添加到导航栏:在Header.vue中添加图片分割页面的导航,方便用户在不同功能页面间切换。
代码语言:javascript
复制

<el-col :span="4">
          <el-menumode="horizontal"router:ellipsis="false">
            <el-menu-itemindex="/resize">
              <span>图片调整</span>
            </el-menu-item>
            <el-menu-itemindex="/compress">
              <span>图片压缩</span>
            </el-menu-item>
            <el-menu-itemindex="/divide">
              <span>图片分割</span>
            </el-menu-item>
          </el-menu>
        </el-col>
20250214233041912148.png
20250214233041912148.png

效果展示

20250214233129811035.png
20250214233129811035.png
20250214233214772684.png
20250214233214772684.png
20250214233314461801.png
20250214233314461801.png

代码托管地址:https://github.com/outeasy/outeasy/releases/tag/v0.4

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小码农薛尧 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码实现
  • 效果展示
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档