一周时间,开发了一款封面图生成工具

65d7e457b690c9d803d4d34d50428547.png

介绍

这是一款封面图的制作工具,根据简单的配置即可生成一张好看的封面图,目前已有七款主题可以选择。做这个工具的初衷来自平时写文章,都为封面图发愁,去图片 网站上搜索很难找到满意的,而且当你要的图如果要搭配上文章的标题,使用 Photoshop 等软件操作成本太大。为此突然来了灵感,何不自己开发一个在线的工具直接生成。

开发前期构思

一款工具型的软件,界面一定要简洁,操作方面。所以在布局上没有必要占满整个页面,宽屏上限定宽度然后相对居中。

内容上软件整体上会分成三块:

  • 预览区域

  • 内容配置区域

  • 样式配置区域

这样一来布局上可以采取列布局或者行布局。我能想到的有:

db73abb306467a44eff5e7b9c143f573.png

由于根据个人喜好最终定下来第二种样式的布局。

代码实现

根据布局,我定义了三个函数组件来实现对应的“预览区”、“内容配置区”和“样式配置区”和一个主页面渲染函数。

// 页面主函数
export function Main(props) {// ...
}
// 内容配置函数
export function ContentForm(props) {// ...
}
// 样式配置函数
export function ConfigForm(props) {// ...
}
// 封面图预览函数
export function CoverImage(props) {// ...
}

这里 UI 组件是引用 Material UI[1],也是本站引用的唯一外部 UI 框架。

页面主函数

主函数中定义了全局共享的配置变量 config 和改变状态的函数 handleConfigChange。它们两会当成参数传入到其它组件中使用。

export function Main({ normal }) {const coverRef = useRef();const [config, setConfig] = useState({font: 'serif',bgColor: '#949ee5',gradientBgColor: '',icon: 'react',ratio: 0.5,width: 800,title: '欢迎来到太空编程站点',author: '编程范儿',theme: 'basic',bgImg: 'https://spacexcode.oss-cn-hangzhou.aliyuncs.com/1704985651753-e2a2eb6d-71c6-4293-8d8c-49203c7410bb.jpeg'});const handleConfigChange = (val, key) => {setConfig((prev) => ({ ...prev, [key]: val }));};const downloadImage = (scale, format) => {// todo};const handleCopyImg = (cb) => {//todo};return (<Box sx={{ padding: '40px 0' }}><Grid container spacing={3}><Grid item xs={12} md={ normal ? 8 : 12 }><Box className={styles.card} sx={{ padding: '20px 10px', overflowX: 'auto' }}>{/* 生成图显示 */}<div ref={coverRef} className={styles.preview} style={{ width: config.width + 'px'}}><CoverImage config={config} /></div></Box><Box className={styles.card} sx={{ padding: '10px 20px 40px', marginTop: '24px' }}><ContentForm config={config} handleConfigChange={handleConfigChange} /></Box></Grid>{/* 配置 */}<Grid item xs={12} md={normal ? 4 : 6}><Box className={styles.card} sx={{ padding: '10px 20px 40px' }}><ConfigForm config={config} handleConfigChange={handleConfigChange} downloadImage={downloadImage} handleCopyImg={handleCopyImg} /></Box></Grid></Grid></Box>)
}

因为页面主函数主要是集成其它三个组件,没有什么逻辑,我们来一一讲讲“内容配置函数”、“样式配置函数”和“封面图预览函数”这三个函数的实现。

内容配置函数

封面图中的内容配置就三项:标题作者图标

标题和作者是两个简单的文本输入框,图标数据是我本地写了一个列表,图标本身是一段 SVG 代码。使用的 React 函数组件返回。

export function ContentForm ({ config, handleConfigChange }) {return (<><Box className={styles.setItem}><Typography variant="h5">标题</Typography><TextFieldvalue={config.title}onChange={e => handleConfigChange(e.target.value, 'title')}placeholder='标题'size='small'multilinerows={3}fullWidth/></Box><Box className={styles.setItem}><Typography variant="h5">作者</Typography><TextFieldvalue={config.author}onChange={e => handleConfigChange(e.target.value, 'author')}placeholder='作者'size='small'fullWidth/></Box><Box className={styles.setItem}><Typography variant="h5">图标</Typography><Box sx={{ display: 'flex', gap: '10px' }}><Selectvalue={config.icon}onChange={val => handleConfigChange(val.target.value, 'icon')}label=''size='small'fullWidth>{devicons.map(el => (<MenuItem value={el.value} key={el.value}><div className={styles.selectIconRow}><span>{el.label}</span>{selectDevicon(el.value)}</div></MenuItem>))}</Select><Button component="label" size='small' variant="contained" sx={{ width: '120px' }} startIcon={<AddPhotoAlternateIcon />}>上传<VisuallyHiddenInput type="file" onChange={(e) => handleConfigChange(URL.createObjectURL(e.target.files[0]), 'customIcon')} /></Button></Box></Box></>)
}

说明下:这里我只贴主要代码。

样式配置函数

样式配置主要是对封面图的 Layout、上面文字的字体、背景色和图片的长宽进行设置。同时这个区域还包含两个操作按钮:图片的复制和导出。

主题

这里我定义了七款主题,分别对它们进行命名“basic”、“background”、“stylish”、“outline”、“modern”、“preview”和“mobile”。后面会根据 命名对主题进行调用。

在配置里,我们对不同的主题设计了 Layout 模型,放在选项中进行选择,另外还分别对它们建立了真实渲染的文件。分别放在 themes 和 themeSkeleton 两个目录下。

我们这里就以 basic 主题进行讲解,其它类似。

import { Skeleton } from '@mui/material';export default const BasicTheme = () => {return (<div className={styles.basicTheme}><Skeleton animation={false} variant="rectangular" sx={{ padding: '8px' }} width={116} height={68}><div className={styles.content}><Skeleton animation={false} variant="text" width={'100%'} height={10} /><Skeleton animation={false} variant="text" width={'70%'} height={10} /><div className={styles.bt}><Skeleton animation={false} variant="rounded" width={10} height={10} /><Skeleton animation={false} variant="text" width={'20%'} height={8} /></div></div></Skeleton></div>);
}

每个 UI 框架都有 Skeleton 骨架屏组件,我们可以直接使用它来生成我们的主题模型。很轻松就实现了布局。

而主题的渲染组件则要通过读取配置来做实现样式的定制。

export default const BasicTheme = ({ config }) => {const { title, bgColor, gradientBgColor, author, icon, font, customIcon, width, ratio } = config;const height = width * ratio + 'px';return (<div className={styles.basicTheme}><div className={styles.main} style={{ backgroundColor: bgColor, backgroundImage: gradientBgColor, height: height }}><div className={clsx(styles.content, styles['font-' + font])}><div style={{ padding: '0 3rem' }}><h1>{title}</h1></div><div className={styles.bt}>{customIcon ?<div className={styles.customIcon}><img src={customIcon} alt="img" /></div>:<div className={styles.devicon}>{selectDevicon(icon)}</div>}<h2 className={styles.author}>{author}</h2></div></div></div></div>);
}

配置中的主题名和实际的主题组件函数做了映射。

const selectTheme = (theme) => {switch (theme) {case 'basic':return <BasicTheme config={config} />;case 'modern':return <ModernTheme config={config} />;case 'outline':return <OutlineTheme config={config} />;case 'background':return <BackgroundTheme config={config} />;case 'preview':return <PreviewTheme config={config} />;case 'stylish':return <StylishTheme config={config} />;case 'mobile':return <MobileTheme config={config} />;default:return <BasicTheme config={config} />;}
};
字体

字体选项中有每个字体的命名,它们被存在配置变量中,在主题渲染函数中会被用在类名中。然后对相应的类名设置对应的 font-family

背景色

背景色有两种类型:纯色和渐变色,分别通过 CSS 的 background-color 和 background-image 属性进行设置。

渐变色我们预定义了八种:

const bgColorOptions = ['linear-gradient(310deg,rgb(214,233,255),rgb(214,229,255),rgb(209,214,255),rgb(221,209,255),rgb(243,209,255),rgb(255,204,245),rgb(255,204,223),rgb(255,200,199),rgb(255,216,199),rgb(255,221,199))','linear-gradient(160deg,rgb(204,251,252),rgb(197,234,254),rgb(189,211,255))','linear-gradient(150deg,rgb(255,242,158),rgb(255,239,153),rgb(255,231,140),rgb(255,217,121),rgb(255,197,98),rgb(255,171,75),rgb(255,143,52),rgb(255,115,33),rgb(255,95,20),rgb(255,87,15))','linear-gradient(345deg,rgb(211,89,255),rgb(228,99,255),rgb(255,123,247),rgb(255,154,218),rgb(255,185,208),rgb(255,209,214),rgb(255,219,219))','linear-gradient(150deg,rgb(0,224,245),rgb(31,158,255),rgb(51,85,255))','linear-gradient(330deg,rgb(255,25,125),rgb(45,13,255),rgb(0,255,179))','linear-gradient(150deg,rgb(0,176,158),rgb(19,77,93),rgb(16,23,31))','linear-gradient(150deg,rgb(95,108,138),rgb(48,59,94),rgb(14,18,38))'
]

纯色的选择放了一个取色器,另外后面还放了一个随机生成颜色的按钮,这里也是人为定了一些颜色,然后从中随机选取。

长宽设置

长度通过 Slider 滑块组件进行设置,为了保证生成的图片大小在合理的范围内,这里设置了最大和最小边界值,区间范围在 [600, 820] 之间。

宽度的实现是通过设置长宽比来实现的。

1:23:54:75:8 这几个比例都能保证图片有较好的效果。

复制和下载

图片生成好之后,我预想了会有两个动作,一个是下载保存到本地,另一个是为了快捷使用,如果是在聊天工具,类似微信、QQ或者钉钉的聊天框中可直接 粘帖复制好的图片。另外富文本编辑器也支持。

这里我们首先要用到核心组件 html2canvas 来帮我们实现从页面 html 元素转为 canvas 对象,进而实现图片的保存和复制。

const handleCopyImg = (cb) => {if (!coverRef.current) return;html2canvas(coverRef.current, {useCORS: true,scale: 1,backgroundColor: 'transparent'}).then((canvas) => {canvas.toBlob(async blob => {console.log(blob);const data = [new ClipboardItem({[blob.type]: blob,}),];await navigator.clipboard.write(data).then(() => {console.log("复制成功!");cb && cb();},() => {console.error("失败.");});});})
};

图片保存的时候会弹出类型和大小选择的选项,支持 png 和 jpg 格式的导出,另外为了在 retina 屏幕上适配,也提供了 2X 图的导出。

const downloadImage = (scale, format) => {if (!coverRef.current) return;html2canvas(coverRef.current, {useCORS: true,scale: scale,backgroundColor: 'transparent'}).then((canvas) => {let newImg = new Image()const date = new Date()newImg.src = canvas.toDataURL('image/' + format) // 'image/png'const a = document.createElement("a");a.style.display = "none";a.href = newImg.src;a.download = `spacexcode-cover-${date.getMinutes()}${date.getSeconds()}.${format}`;a.rel = "noopener noreferrer";document.body.append(a);a.click();setTimeout(() => {a.remove();}, 1000);})
};

为了做一款好用的工具,还是尽量多想想,包含一些特殊的使用场景。

工具地址:https://spacexcode.com/coverview

参考资料

[1]

Material UI: https://mui.com/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/640278.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JavaEE进阶】 关于⽇志框架(SLF4J)

文章目录 &#x1f333;SLF4j&#x1f332;⻔⾯模式(外观模式)&#x1f6a9;⻔⾯模式的定义&#x1f6a9;⻔⾯模式的优点 &#x1f343;关于SLF4J框架&#x1f6a9;不引⼊⽇志⻔⾯&#x1f6a9;引⼊⽇志⻔⾯ ⭕总结 &#x1f333;SLF4j SLF4J不同于其他⽇志框架,它不是⼀个真正…

构建高效外卖系统:技术实践与代码示例

外卖系统在现代社会中扮演着重要的角色&#xff0c;为用户提供了便捷的用餐解决方案。在这篇文章中&#xff0c;我们将探讨构建高效外卖系统的技术实践&#xff0c;同时提供一些基础的代码示例&#xff0c;帮助开发者更好地理解和应用这些技术。 1. 技术栈选择 构建外卖系统…

BP蓝图映射到C++笔记1

教程链接&#xff1a;示例1&#xff1a;CompleteQuest - 将蓝图转换为C (epicgames.com) 1.常用的引用需要记住&#xff0c;如图所示。 2.蓝图中可以调用C函数&#xff0c;也可以实现C函数 BlueprintImplementableEvent:C只创建&#xff0c;不实现&#xff0c;在蓝图中实现 B…

C++提高编程---模板---类模板

目录 一、类模板 1.模板 2.类模板的作用 3.语法 4.声明 二、类模板和函数模板的区别 三、类模板中成员函数的创建时机 四、类模板对象做函数参数 五、类模板与继承 六、类模板成员函数类外实现 七、类模板分文件编写 八、类模板与友元 九、类模板案例 一、类模板 …

软件测试的需求人才越来越多,为什么大家还是不太愿意走软件测试的道路?

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

【动态规划】【C++算法】801. 使序列递增的最小交换次数

作者推荐 【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径 本文涉及知识点 动态规划汇总 数组 LeetCode801使序列递增的最小交换次数 我们有两个长度相等且不为空的整型数组 nums1 和 nums2 。在一次操作中&#xff0c;我们可以交换 nums1[i] 和 num…

路飞项目--03

二次封装Response模块 # drf提供的Response&#xff0c;前端想接收到的格式 {code:xx,msg:xx} 后端返回&#xff0c;前端收到&#xff1a; APIResponse(tokneasdfa.asdfas.asdf)---->{code:100,msg:成功,token:asdfa.asdfas.asdf} APIResponse(code101,msg用户不存在) ---…

学习笔记-李沐动手学深度学习(一)(01-07,概述、数据操作、tensor操作、数学基础、自动求导)

个人随笔 第三列是 jupyter记事本 官方github上啥都有&#xff08;代码、jupyter记事本、胶片&#xff09; https://github.com/d2l-ai 多体会 【梯度指向的是值变化最大的方向】 符号 维度 &#xff08;弹幕说&#xff09;2&#xff0c;3&#xff0c;4越后面维度越低 4…

Java 面向对象案例 02 (黑马)

代码&#xff1a; public class foodTest {public static void main(String[] args) {//1、构建一个数组food[] arr new food[3];//2、创建三个商品对象food f1 new food("apple","123",3.2,500);food f2 new food("pear","456",4…

临时工说:AI 人工智能化对于DBA 的工作的影响

这开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题&#xff0c;有需求都可以加群群内&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;&#xff08;共1900人左右 1 2 3 4 5&#xf…

ChatGPT:关于 OpenAI 的 GPT-4工具,你需要知道的一切

ChatGPT&#xff1a;关于 OpenAI 的 GPT-4工具&#xff0c;你需要知道的一切 什么是GPT-3、GPT-4 和 ChatGPT&#xff1f;ChatGPT 可以做什么&#xff1f;ChatGPT-4 可以做什么&#xff1f;ChatGPT 的费用是多少&#xff1f;GPT-4 与 GPT-3.5 有何不同&#xff1f;ChatGPT 如何…

开源堡垒机JumpServer本地安装并配置公网访问地址

文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机&#xff0c;是符合 4A 规范的专业运维安全审计系统。JumpS…

ONLYOFFICE服务器无法连接,请联系管理员问题解决

1、现象 部署好了nextcloud和onlyoffice后&#xff0c;新建文本文档报错ONLYOFFICE服务器无法连接&#xff0c;请联系管理员。 用快捷键“F12”进入控制台&#xff0c;点开错误提示栏&#xff0c;找到有“api.js“文件&#xff0c;“https://ONLYOFFICED的地址/web-apps/apps/…

书法AI全自动切字+识别算法2.0版发布,草书篆书行书楷书识别准确率超过90%,覆盖书法单字30万张

我们开发的业界识别最准覆盖作品最全的书法AI小程序上线了 书法AI全自动切字识别算法2.0版发布&#xff0c;草书篆书行书楷书识别准确率超过90%&#xff0c;准确率甩百度OCR一条街&#xff0c;覆盖书法单字30万张&#xff0c;遥遥领先同行 我们还可为客户提供书法AI全自动切字a…

借助文档控件Aspose.Words,将 Word DOC/DOCX 转换为 TXT

在文档处理领域&#xff0c;经常需要将 Word 文档转换为更简单的纯文本格式。无论是出于数据提取、内容分析还是兼容性原因&#xff0c;将 Word&#xff08;.doc、.docx&#xff09;文件转换为纯文本&#xff08;.txt&#xff09;的能力对于开发人员来说都是一项宝贵的技能。在…

87230系列USB连续波功率探头

01 87230 USB连续波功率探头 产品综述&#xff1a; 87230/87231/87232/87233系列USB功率探头是一款基于USB2.0全速/高速自适应接口的二极管检波式功率探头&#xff0c;内部采用高性能处理芯片&#xff0c;通过各种校准和补偿技术&#xff0c;使得探头具有频率范围宽、功率动…

基于SpringBoot的民宿预定管理系统 JAVA简易版

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用例设计2.2 功能设计2.2.1 租客角色2.2.2 房主角色2.2.3 系统管理员角色 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿4.3 新增民宿评价4.4 查询留言4.5 新增民宿订单 五、免责说明 一、摘要 1.1 项目介绍 基于…

如何在CentOS8使用宝塔面板本地部署Typecho个人网站并实现公网访问【内网穿透】

文章目录 前言1. 安装环境2. 下载Typecho3. 创建站点4. 访问Typecho5. 安装cpolar6. 远程访问Typecho7. 固定远程访问地址8. 配置typecho 前言 Typecho是由type和echo两个词合成的&#xff0c;来自于开发团队的头脑风暴。Typecho基于PHP5开发&#xff0c;支持多种数据库&#…

重拾计网-第四弹 计算机网络性能指标

ps&#xff1a;本文章的图片内容来源都是来自于湖科大教书匠的视频&#xff0c;声明&#xff1a;仅供自己复习&#xff0c;里面加上了自己的理解 这里附上视频链接地址&#xff1a;1.5 计算机网络的性能指标&#xff08;1&#xff09;_哔哩哔哩_bilibili ​​​ 目录 &#x…

最全笔记软件盘点!你要的笔记神器都在这里:手写笔记、知识管理、文本笔记、协作笔记等!

在当今的信息化社会中&#xff0c;人们对信息的处理速度越来越快&#xff0c;从工作到生活&#xff0c;我们都面临着大量信息的冲击。在这样的环境下&#xff0c;一个能够帮助我们管理、整理和储存信息的好工具显得尤为重要&#xff0c;而笔记软件恰恰可以满足这些需求。 在选…