在开始之前,确保你的开发环境已经安装了以下工具:
npm install -g yarn
进行全局安装。同时,为了方便管理 Node.js 版本,推荐安装 nvm
(Node Version Manager),安装命令如下:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
安装完成后,重新打开终端或者执行 source ~/.nvm/nvm.sh
使其生效。
环境搭建好后,就可以开始创建 Nuxt.js 项目啦。打开终端,依次执行下面这些命令:
mkdir outeasy
cd outeasy
npx nuxi@latest init
npm install
这些命令分别是什么意思呢?
项目创建好后,会有一些初始文件。
比如app.vue文件,它的初始代码如下:
<template>
<div>
<NuxtRouteAnnouncer />
<NuxtWelcome />
</div>
</template>
执行npm run dev
启动服务,就能在浏览器里看到默认页面了。
这个默认页面是 Nuxt.js 帮我们生成的,展示了一些基本信息。
现在,我们要给项目添加一个新页面,这个页面能上传图片、调整图片大小,还能提供下载。
首先,创建pages目录,并在里面创建resize.vue文件。pages目录在 Nuxt.js 里很重要,Nuxt.js 会根据这个目录下的文件自动生成路由。
resize.vue
的内容如下:
<template>
<div class="container mx-auto p-4 max-w-2xl">
<h1 class="text-2xl font-bold mb-6">图片尺寸调整</h1>
<!-- 图片上传区域 -->
<div
class="border-2 border-dashed border-gray-300 rounded-lg p-8 mb-6 text-center"
@drop.prevent="handleDrop"
@dragover.prevent
@dragenter.prevent
>
<input
type="file"
ref="fileInput"
@change="handleFileSelect"
accept="image/*"
class="hidden"
/>
<div v-if="isLoading" class="text-center py-8">
<div class="inline-block animate-spin rounded-full h-8 w-8 border-4 border-blue-500 border-t-transparent"></div>
<p class="mt-2 text-gray-600">正在处理图片...</p>
</div>
<div v-else-if="errorMessage" class="text-center py-8">
<p class="text-red-500">{{ errorMessage }}</p>
<button
@click="errorMessage = '';"
class="mt-4 text-blue-500 hover:text-blue-600"
>
重试
</button>
</div>
<div v-else-if="!imageUrl" class="space-y-4">
<div class="text-gray-500">
拖拽图片到此处或
<button
@click="$refs.fileInput.click()"
class="text-blue-500 hover:text-blue-600"
>
点击上传
</button>
</div>
<p class="text-sm text-gray-400">支持 JPG、PNG 格式</p>
</div>
<img
v-else
:src="imageUrl"
class="max-w-full max-h-[300px] mx-auto"
alt="预览图"
/>
</div>
<!-- 尺寸调整控制面板 -->
<div v-if="imageUrl" class="space-y-4">
<div class="flex space-x-4">
<div class="flex-1">
<label class="block text-sm font-medium text-gray-700 mb-1">宽度 (px)</label>
<input
type="number"
v-model="width"
@input="handleWidthChange"
class="w-full px-3 py-2 border border-gray-300 rounded-md"
/>
</div>
<div class="flex-1">
<label class="block text-sm font-medium text-gray-700 mb-1">高度 (px)</label>
<input
type="number"
v-model="height"
@input="handleHeightChange"
class="w-full px-3 py-2 border border-gray-300 rounded-md"
/>
</div>
</div>
<div class="flex items-center space-x-2">
<input
type="checkbox"
id="maintainAspectRatio"
v-model="maintainAspectRatio"
class="rounded text-blue-500"
/>
<label for="maintainAspectRatio" class="text-sm text-gray-700">
保持宽高比
</label>
</div>
<button
@click="downloadImage"
class="w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 transition-colors"
>
下载调整后的图片
</button>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const fileInput = ref(null)
const imageUrl = ref('')
const width = ref(0)
const height = ref(0)
const maintainAspectRatio = ref(true)
const aspectRatio = ref(1)
const isLoading = ref(false)
const errorMessage = ref('')
// 文件大小限制(10MB)
const MAX_FILE_SIZE = 10 * 1024 * 1024
// 支持的图片格式
const SUPPORTED_FORMATS = ['image/jpeg', 'image/png', 'image/webp']
// 处理文件选择
const handleFileSelect = (event) => {
const file = event.target.files[0]
if (file) {
processImage(file)
}
}
// 处理拖拽上传
const handleDrop = (event) => {
const file = event.dataTransfer.files[0]
if (file && file.type.startsWith('image/')) {
processImage(file)
}
}
// 处理图片
const validateFile = (file) => {
if (!SUPPORTED_FORMATS.includes(file.type)) {
errorMessage.value = '请上传 JPG、PNG 或 WebP 格式的图片'
return false
}
if (file.size > MAX_FILE_SIZE) {
errorMessage.value = '图片大小不能超过 10MB'
return false
}
errorMessage.value = ''
return true
}
const processImage = (file) => {
if (!validateFile(file)) return
isLoading.value = true
errorMessage.value = ''
const reader = new FileReader()
reader.onload = (e) => {
const img = new Image()
img.onload = () => {
width.value = img.width
height.value = img.height
aspectRatio.value = img.width / img.height
isLoading.value = false
}
img.onerror = () => {
errorMessage.value = '图片加载失败,请重试'
isLoading.value = false
}
img.src = e.target.result
imageUrl.value = e.target.result
}
reader.onerror = () => {
errorMessage.value = '图片读取失败,请重试'
isLoading.value = false
}
reader.readAsDataURL(file)
}
// 处理宽度变化
const handleWidthChange = () => {
if (maintainAspectRatio.value) {
height.value = Math.round(width.value / aspectRatio.value)
}
}
// 处理高度变化
const handleHeightChange = () => {
if (maintainAspectRatio.value) {
width.value = Math.round(height.value * aspectRatio.value)
}
}
// 下载调整后的图片
const downloadImage = () => {
const canvas = document.createElement('canvas')
canvas.width = width.value
canvas.height = height.value
const ctx = canvas.getContext('2d')
const img = new Image()
img.onload = () => {
ctx.drawImage(img, 0, 0, width.value, height.value)
const link = document.createElement('a')
link.download = 'resized-image.png'
link.href = canvas.toDataURL('image/png')
link.click()
}
img.src = imageUrl.value
}
</script>
这个页面代码比较长,大致分为两部分:模板(template)和脚本(script)。模板部分定义了页面的结构和样式,包括图片上传区域、尺寸调整控制面板;脚本部分用 Vue 的组合式 API 定义了各种功能逻辑,比如文件选择处理、图片处理、尺寸调整、图片下载等。
添加完resize.vue
后,还需要修改app.vue
文件,移除NuxtWelcome组件,只保留NuxtPage组件,确保路由能正确渲染我们的图片尺寸调整页面。修改后的app.vue如下:
<template>
<div>
<NuxtPage />
</div>
</template>
完成上述步骤后,重新启动服务,然后就可以测试图片处理功能了。上传图片,输入想要调整的宽度和高度,勾选或不勾选 “保持宽高比”,点击 “下载调整后的图片”,就能得到处理后的图片。
上传图片
打开处理后的图片
托管地址:https://github.com/outeasy/outeasy/releases/tag/v0.1