🎉🎉🎉在线简历开箱即用(宇智波鼬示例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中的双重循环,分别用于 <math xmlns="http://www.w3.org/1998/Math/MathML"> r (行)和 r(行)和 </math>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效果。这里的旋转角度基于 <math xmlns="http://www.w3.org/1998/Math/MathML"> r 和 r和 </math>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尽量写简洁易懂,配置信息另存文件

参考

相关推荐
耶啵奶膘40 分钟前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^2 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie3 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic3 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿4 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具4 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161775 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test5 小时前
js下载excel示例demo
前端·javascript·excel
Yaml45 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事5 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro