手把手实现一个Box Shadow Generator

1,599 阅读3分钟

一般我们给一个元素添加阴影效果第一时间想到box-shadow属性,但因其众多参数(X,Y轴偏移、模糊和扩散半径以及颜色参数)不好调试,所以一般也是看网上好看的阴影效果粘贴过来就是了。。今天准备写一个box-shadow可视化代码生成器,自己也能调试出好看的阴影效果!

线上地址 8jndl8.csb.app/

效果展示 2023-11-06 14.13.51.gif

html结构

划分的结构如图所示

image.png

结构清楚了代码也就好写咯

<body>
		<div class="container">
			<div class="result">
				<div id="preview"></div>
			</div>
			<div class="settings">
				<div class="range-wrapper">
					<label for="x-shadow">Horizontal Shadow :</label>
					<input type="range" id="x-shadow" min="-100" max="100" value="-6" />
				</div>
				<div class="range-wrapper">
					<label for="y-shadow">Vertical Shadow :</label>
					<input type="range" id="y-shadow" min="-100" max="100" value="15" />
				</div>
				<div class="range-wrapper">
					<label for="blur-r">Blur Radius :</label>
					<input type="range" id="blur-r" min="0" max="100" value="30" />
				</div>
				<div class="range-wrapper">
					<label for="spread-r">Spread Radius :</label>
					<input type="range" id="spread-r" min="-50" max="50" value="6" />
				</div>
				<div class="range-wrapper">
					<label for="border-r">Border Radius :</label>
					<input type="range" id="border-r" min="0" max="100" step="1" value="25" />
				</div>
				<div class="range-wrapper">
					<label for="shadow-opacity">Shadow Opacity :</label>
					<input type="range" id="shadow-opacity" min="0" max="1" step="0.1" value="0.5" />
				</div>

				<div class="input-wrapper">
					<label for="inset-shadow">Inset Shadow :</label>
					<input type="checkbox" id="inset-shadow" />
				</div>

				<div id="color-wrapper">
					<label for="shadow-color">Shadow Color :</label>
					<input type="color" id="shadow-color" />
				</div>
			</div>

			<div class="code-container">
				<textarea id="styles" rows="2"></textarea>
				<button id="copy-styles" onclick="copyStyles()">Copy Styles</button>
			</div>
		</div>

		<script src="index.js"></script>
	</body>

需要注意的点

  • <input type='range'> 就是那个滑块,可以指定最小值,最大值,步进值,初始值(min, max, step, value)
  • <input type='color'> 是颜色选择器, 可选RGB,hSL,HEX
  • <textarea rows="2"></textarea> 文本框,输入区域设置默认显示2行文本

style样式

  • .settings.code-container 用grid布局实现,grid-template-columns按比例分布
  • .settings里面的.range-wrapperinput标签等用flex布局 主轴为列 再space-between实现
  • 点击copy会有个animation, 动画周期到50%处button放大1.1倍
:root {
	--bg: #311b92;
}
body {
	background-color: var(--bg);
}
label {
	font-size: 14px;
}
input {
	cursor: pointer;
}
input[type='color'] {
	width: 25px;
	height: 25px;
	border: 1px solid #ccc;
}
textarea {
	resize: none;
	padding: 5px;
	border: 1px solid #000;
	border-radius: 5px;
}
.container {
	background-color: #fff;
	position: absolute;
	transform: translate(-50%, -50%);
	top: 50%;
	left: 50%;
	padding: 30px;
	width: 80vmin;
	border-radius: 25px;
}
.result {
	padding: 20px 0 120px;
}
#preview {
	background-color: var(--bg);
	width: 200px;
	height: 200px;
	position: relative;
	margin: auto;
}
.settings {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 15px 25px;
}
.range-wrapper {
	display: flex;
	flex-direction: column;
	justify-content: space-between;
}
.code-container {
	display: grid;
	grid-template-columns: 3fr 1fr;
	gap: 10px;
	margin-top: 20px;
}
.code-container button {
	background-color: var(--bg);
	border-radius: 5px;
	cursor: pointer;
	border: none;
	color: #fff;
}
.input-wrapper,
#color-wrapper {
	display: flex;
	align-items: center;
	gap: 10px;
}
.copy-success-animation {
	animation: copied 1s ease-in-out;
}
@keyframes copied {
	50% {
		transform: scale(1.1);
	}
}

js

当每个input滑块改变时都要响应式地应用效果,都需要监听input事件的触发, 给每一个input都绑定generateStyles事件来响应效果

const ranges = document.querySelectorAll('.settings input')
ranges.forEach(slider => {
	slider.addEventListener('input', generateStyles)
})

generateStyles实现

  • 获取DOM元素value值,然后拼装成box-shadow参数
  • DOM.style.语法 给DOM添加box-shadowborder-radius样式
  • DOM.textContent语法 替换code-container中的文字框文本
const preview = document.getElementById('preview') // demo方块
const styles = document.getElementById('styles') // box-shadow 代码文本框
function generateStyles() {
	const xShadow = document.getElementById('x-shadow').value
	const yShadow = document.getElementById('y-shadow').value
	const blurRadius = document.getElementById('blur-r').value
	const spreadRadius = document.getElementById('spread-r').value
	const shadowColor = document.getElementById('shadow-color').value
	const shadowOpacity = document.getElementById('shadow-opacity').value
	const shadowInset = document.getElementById('inset-shadow').checked
	const borderRadius = document.getElementById('border-r').value

	const boxShadow = `${
		shadowInset ? 'inset ' : ''
	} ${xShadow}px ${yShadow}px ${blurRadius}px ${spreadRadius}px ${hexToRgba(
		shadowColor,
		shadowOpacity
	)}`

	preview.style.boxShadow = boxShadow
	preview.style.borderRadius = `${borderRadius}px`
	styles.textContent = `box-shadow: ${boxShadow};\nborder-radius: ${borderRadius}px;`
}

由于shadowColor其实是HEX格式,需要写个hexToRgba方法转化为rgba格式

hexToRgba

  • substr(start, length) 第一个参数是起始索引,第二个参数是截取长度
  • parseInt(hexStr, 16)将十六进制转换为十进制 例子 : shadowColor = '#2a203c' 其中 r = '2a'; g = '20'; b = '3c'
// 16进制转换为rgba
function hexToRgba(shadowColor, shadowOpacity) {
	const r = parseInt(shadowColor.substr(1, 2), 16)
	const g = parseInt(shadowColor.substr(3, 2), 16)
	const b = parseInt(shadowColor.substr(5, 2), 16)
	return `rgba(${r}, ${g}, ${b}, ${shadowOpacity})`
}

copyStyles复制到剪切板

  • 当我们点击复制样式按钮时,通过 Clipboard Api实现复制到剪切板
  • .then等待执行完,copyButton直接添加animation样式,更换innerText,
  • 再用setTimeout1s之后移除animation和复原innerText。 那为啥1s? 因为动画也是1s 保持统一咯
function copyStyles() {
	styles.select()
	const textToCopy = styles.value
	navigator.clipboard
		.writeText(textToCopy)
		.then(() => {
			copyButton.innerText = '复制成功!🎉'
			copyButton.classList.add('animation')

			setTimeout(() => {
				copyButton.innerText = '复制样式'
				copyButton.classList.remove('animation')
			}, 1000)
		})
		.catch(() => {
			copyButton.innerText = '复制失败 😔'
		})
}

代码片段

box-shadow推荐网站

box-shadow.art 样式超多,一键复制,还可自己DIY