🎉🎉🎉在线简历开箱即用(宇智波鼬示例demo)

本项目使用React + Vite快速代建,之后会适配vanilla版本开箱即用。主要实现效果:鼠标悬停hover 3D视觉体验,统一UI设计,动画平滑,卡片易扩展

codesandbox沙箱仓库

直接上效果

因为最近在准备写简历,然后看网上要么就是word模板或者直接md文档生成pdf/word,感觉比较没意思。索性做一个在线简历,以示前端雄风、、、? 为了不涉及个人隐私就找了动漫人物宇智波鼬来练练手(小时候很喜欢😍)

前期准备

技术选型

因为项目很小,不涉及用户交互只是简单的页面展示,就选用小而巧的vite搭建

bash 复制代码
npm create vite@latest yuzhiboyou

把没有用的css和测试文件删掉就开始code

信息收集

google一下鼬的帅照

因为已经好久没追火影了,宇智波鼬的好多技能都忘记了。。。。还是查一下资料

梳理一下主要信息,把重复的内容抽取出来成一个单独的config配置文件,方便之后渲染

按照程序员写技术了解程度的惯词 (精通,擅长,熟悉,了解)来描述宇智波鼬的忍术🫰

jsx 复制代码
const config = {
	baseInfo: [
		'生日 :6月9日(双子座)',
		'等级:暗部B级、S级叛忍',
		'忍者登记号码:012110',
		'兴趣:逛甜品屋、回想以前愉快的事情',
	],
	resume: [
		{
			name: '木叶忍者',
			intro: '木叶村的天才 | 六岁 ~ 十岁',
			details: [
				'七岁以第一名的成绩从忍者学校毕业(六岁入学)',
				'八岁时因目睹带土杀光他的同伴而开启写轮眼',
				'十岁晋升为中忍,被誉为为"天才忍者"',
			],
		},
		{
			name: '暗部',
			intro: '担任暗部分队长 | 十岁 ~ 十三岁',
			details: ['守卫木叶村安宁', '为顾全大局同宇智波带土灭族,唯独留下弟弟佐助'],
		},

		{
			name: '晓组织成员',
			intro: '为保护木叶以间谍加入晓组织 | 十三岁 ~ 二十一岁',
			details: [
				'为晓组织献身 但未真正捕捉尾兽',
				'协助佐助觉醒万花筒写轮眼',
				'在世界大战中作出重大贡献',
			],
		},
	],
	skills: [
		'万花筒血轮眼',
		'天照',
		'须佐能乎',
		'豪火球之术',
		'鸦分身之术',
		'豪龙火之术',
		'水龙弹之术',
		'影分身之术',
		'月读',
		'幻术',
	],
	skillStack: [
		'精通血继结界 如万花筒写轮眼(天照,伊邪那美,须佐能乎,八坂勾玉)',
		'精通火遁忍术 例如凤仙火之术 豪火球之术 凤仙花爪红 豪龙火之术 豪焰球',
		'擅长封印型 转写封印 别天神',
		'擅长组合忍术 天照·二重 须佐能乎·双神雷临 凌乱雪月花',
		'熟悉辅助型忍术 熟悉的有鸦分身之术 分身大爆破 影分身之术 水分身之术 替身术',
		'了解水遁忍术 例如水牙弹 水龙弹之术',
	],
	chakras: ['风', '火', '水', '阴', '阳'],
}
export default config

resume结构设计

简历主要分左右俩大板块,左边是人物图像和基本信息,右边是五个卡片card组成的人物细节描述

左部分jsx结构

jsx 复制代码
import imgUrl from './img/Yuzhi-Wave-Weasel.jpg'
export default function Resume(){
    return (
    <div>
        <main>
            <div className="profile">
                <div className="card">
                            <img src={imgUrl}></img>
                    <h2>
                        宇智波鼬 <small>うちはイタチ</small>
                    </h2>
                    <h3>宇智波一族 晓组织</h3>
                    <h5>火之国·木叶隐村</h5>
                    <br />
                    <p>
                        <small>
                            宇智波佐助的兄长,特征是黑色长发,从近鼻翼的眼角延伸至眼下的深邃纹路(泪沟),代表「朱雀」的「朱」字戒指,佩戴在右手无名指上,脖子戴有项链。为人沉着冷静,天资聪颖的他,五岁就将家族基本忍术:「火遁」豪火球之术一学就会,七岁以第一名的成绩从忍者学校毕业(六岁入学),八岁时因目睹带土杀光他的同伴而开启写轮眼,十岁晋升为中忍,被称为「天才忍者」,十三岁就当上暗部分队长,是家族的骄傲。不料却突然在一个晚上之内将家族几乎全灭后,离开木叶成为叛忍,并加入「晓」(实则是为了保护木叶而加入晓作为间谍)。他的弟弟宇智波佐助成为家族中唯一的活口,自始不惜一切以杀死他为目标,为族人复仇。
                        </small>
                    </p>
                    <br />
                    <p>
                        <div>基本信息</div>
                        {config.baseInfo.map((item, index) => (
                            <a key={index}>{item}</a>
                        ))}
                    </p>
                </div>
            </div>
        </main>
    </div>
)}

css和框架

css基本样式

  • 将默认浏览器css重置 确定简历基本色调
  • grid实现响应式俩栏布局
  • media实现移动端适配
    • grid-template-columns: 320px 1fr;当屏幕宽度大于 768px 时,main 元素的列布局将改为两列。左边的的宽度为 320px,右边的宽度使用剩余空间(1fr),小于768px则是一列
css 复制代码
*,
*::before,
*::after {
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}
body {
	line-height: 1.4;
	font-weight: 420;
	font-family: 'DM Sans', sans-serif;
	background-color: #0f0f0f;
	color: #e4dcdc;
}

main {
	max-width: 1000px;
	margin: 0 auto;
	padding: 2rem;
	display: grid;
	gap: 2rem;
}
img {
	display: block;
	max-width: 100%;
	height: 100%;
}
h1,h2,h3,h4,h5,h6 {
	color: white;
	font-weight: 700;
}
text {
	font-size: small;
}

@media (min-width: 768px) {
	main {
		grid-template-columns: 320px 1fr;
	}
}

效果展示

card样式

  • 首次加载动画
    • entry关键帧从小到大 透明到透明
    • 缓动函数ease 从慢到快 再从快到慢
  • card 背景图像
    • 用伪元素before来定位
    • background-image: linear-gradient(120deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.05));
      • 120deg:从左上角向右下角渐变
      • 从第二个参数颜色渐变到第三个参数
css 复制代码
.card {
	padding: 1.5rem;
	border-radius: 2rem;
	position: relative;
	box-shadow: -1px -1px 1px rgba(255, 255, 255, 0.1);
	overflow: hidden;
	animation: entry 1s ease;
}
.card::before {
	content: '';
	position: absolute;
	left: 0;
	top: 0;
	width: 100%;
	height: 100%;
	z-index: -1;
	background-image: linear-gradient(120deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.05));
	opacity: 0.25;
	border-radius: 2rem;
}
@keyframes entry {
	from {
		transform: scale(0.5);
		opacity: 0;
	}
	to {
		transform: scale(1);
		opacity: 1;
	}
}

a标签动画

实现效果

  • 当悬停于a标签时,伪元素before显现opacity: 1;
  • 在a标签之前显示选中图标'>' (伪元素before实现)
  • transition: padding-left 0.2s ease, color 0.2s ease;:链接文本的左内边距和颜色会以0.2秒的时间以 "ease" 缓动函数的方式进行平滑过渡
css 复制代码
a {
	color: #9f4941;
	text-decoration: none;
	padding-left: 0.2rem;
	display: block;
	position: relative;
	transition: padding-left 0.2s ease, color 0.2s ease;
}
a::before {
	content: '>';
	position: absolute;
	bottom: 0;
	left: 0;
	opacity: 0;
	color: white;
	transition: opacity 0.2s ease;
}
a:hover {
	color: hotpink;
	padding-left: 1.2rem;
}
a:hover::before {
	opacity: 1;
}

左部分-- 人物图像3D设计

参照 codepen的实现

  1. .reflection-grid-cell-#{( ($r*10) + $c - 10)} { top: ($r * 10%)-10%; left: ($c * 10%)-10%; }通过sass中的双重循环,分别用于 r (行)和 r(行)和 r(行)和c(列)标记一个10*10的网格,并分别设置类名reflection-grid-cell-1,2,3... 在图像之上(z-index)均匀分布100个行内标签,当然也可以更加颗粒

  2. transform: rotateX((($r * -5)+45deg)) rotateY((-45deg+ ($c * 5));监听每个标签的hover属性,悬停时会变换X,Y轴坐标使产生3D效果。这里的旋转角度基于 r 和 r和 r和c的值,是动态的

  3. &:before:{transform: translateY(45-(5% * $r));}设置伪元素的垂直位移,根据$r伪元素将产生垂直位移效果

fragment code

jsx 复制代码
const ImgCard = () => {
	return (
		<div className="reflection-container">
			{Array.from({ length: 100 }, (_, cell) => (
				<span key={cell} className={`reflection-grid-cell reflection-grid-cell-${cell}`}></span>
			))}
			<img src={imgUrl} className="reflection-content"></img>
		</div>
	)
}
scss 复制代码
.reflection-container {
	position: relative;
	display: inline-block;
	vertical-align: middle;
	transform-style: preserve-3d;
	perspective: 1000px;
	height: 100%;

	.reflection-content {
		height: 50vh;
		width: 90vw;
		background-size: cover;
		background-position: center;
		transform: rotateX(0) rotateY(0);
		pointer-events: none;
		transition: 100ms linear transform;
		overflow: hidden;
	}
	.reflection-grid-cell {
		position: absolute;
		z-index: 1;
		width: 10%;
		height: 10%;
	}
	@for $r from 1 to 11 {
		@for $c from 1 to 11 {
			.reflection-grid-cell-#{( ($r*10) + $c - 10)} {
				top: ($r * 10%)-10%;
				left: ($c * 10%)-10%;
			}
			.reflection-grid-cell-#{( ($r*10) + $c - 10)}:hover ~ .reflection-content {
				transform: rotateX((($r * -5)+45deg)) rotateY((-45deg+ ($c * 5)));
				&:before {
					transform: translateY(45-(5% * $r));
				}
			}
		}
	}
}

右部分卡片信息样式

  • h4之下有个a标签,可以复用上面提到的a标签样式
  • list-style-type: disclosure-closed; ul的标签样式选择图中的的实心大于符号
  • .info .card.red::after{}给每个card右上角加不同颜色的书签页样式

jsx和相关css代码片段

jsx 复制代码
<div className="info">
    <div className="card red">
        <h3>人生履历</h3>
        {config.resume.map(item => (
            <div className="block" key={item.name}>
                <h4>
                    <a href="#">{item.name}</a>
                </h4>
                <p>{item.info}</p>
                <ul>
                    {item.details.map((item, index) => (
                        <li key={index}>{item}</li>
                    ))}
                </ul>
            </div>
        ))}
    </div>

    <div className="card orange">
        <h3>教育经历</h3>
        <div className="block">
            <h4>
                <a href="#">忍者学校</a>
            </h4>
            <p>GPA:4.9(5.0)</p>
            <p>国家级奖学金 * 3 , 获ACM世界总决赛金牌</p>
            <ul>
                <li>同龄人中的佼佼者</li>
                <li>不到一年晋升中忍</li>
            </ul>
        </div>
    </div>
</div>
css 复制代码
.block + .block {
	margin-top: 1rem;
}

.block ul {
	font-weight: 300;
}
.block p {
	color: white;
}

ul {
	padding-left: 1rem;
	list-style-type: disclosure-closed;
}
.info .card + .card {
	margin-top: 2rem;
}

ul.pills {
	list-style-type: none;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 0.25rem 0.5rem;
	align-items: baseline;
}

ul.pills > li {
	background-color: #534f4f;
	border-radius: 1rem;
	padding: 0.25rem 0.75rem;
	color: white;
	font-size: 0.8rem;
	display: block;
}

.info .card::after {
	content: '';
	width: 0;
	height: 0;
	border-style: solid;
	border-width: 0 4rem 4rem 0;
	position: absolute;
	right: 0;
	top: 0;
}
.info .card.red::after {
	border-color: transparent red transparent transparent;
}

.info .card.orange::after {
	border-color: transparent orange transparent transparent;
}
.info .card.pink::after {
	border-color: transparent pink transparent transparent;
}
.info .card.green::after {
	border-color: transparent limegreen transparent transparent;
}

.info .card.teal::after {
	border-color: transparent turquoise transparent transparent;
}

做完一个card卡片样式其他的就是粘贴复制按需增删改一下样式就ok辽🤓 如果对你的简历写法有帮助的话可以点赞收藏哦😉

总结

  • 多看多尝试比较有视觉冲击的设计(codepen,css tricks,codrops),思想不要被固化
  • 能用css做就尽量不用js(3D card)
  • 多考虑样式的可复用性 jsx尽量写简洁易懂,配置信息另存文件

参考

相关推荐
To_OC5 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic5 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy5 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志6 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble7 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯8 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜8 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司9 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
kaiking_g9 小时前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
江华森9 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http