Gre*_*lff 4 html css svg reactjs
有没有什么方法可以制作一个松鼠,同时让边框、阴影或内部发光也符合松鼠的形状?
创建圆角可以通过 CSS 使用 houdini 来完成,但是浏览器的支持paintWorklet非常差,使用率仅为 71.35% (2023),可能是因为与绘制工作集有关的安全漏洞。
其他替代方案包括使用具有方圆形状的SVG<clipPath>,但添加方圆边框必须使用原始剪辑路径重新绘制,这使得转换变得困难。当元素缩放时,笔划路径和剪辑路径需要重新渲染。元素的 CSS 属性需要转换为路径数据。
<svg xmlns="http://www.w3.org/2000/svg" width="220" height="220" viewBox="-10 -10 220 220">
<defs>
<clipPath id="squircle-clip">
<path d="M20,0
L180,0
Q200,0 200,20
L200,180
Q200,200 180,200
L20,200
Q0,200 0,180
L0,20
Q0,0 20,0"
style="vector-effect: non-scaling-stroke;"
/>
</clipPath>
</defs>
<rect x="0" y="0" width="200" height="200" fill="#222" clip-path="url(#squircle-clip)"
/>
<path d="M20,0
L180,0
Q200,0 200,20
L200,180
Q200,200 180,200
L20,200
Q0,200 0,180
L0,20
Q0,0 20,0"
fill="none" stroke="#484848" stroke-width="2" style="vector-effect: non-scaling-stroke;"
/>
</svg>Run Code Online (Sandbox Code Playgroud)
是否有其他方法来创建带边框的方圆?Houdini 是一个难以选择的解决方案,因为它只拥有约 71% 的用户,并且不支持 Safari(iOS 和 macOS)或 Firefox。
如果您不需要任何笔画或滤镜/效果(例如投影),则使用剪辑路径效果很好。
\n如果您的最终目标是创建类似 iOS 的 icon svg 可能是您的最佳选择:
\n.resize {\n border: 1px solid #ccc;\n resize: both;\n overflow: auto;\n width: 50%;\n max-width: 50%;\n}\n\nsvg {\n width: 100%;\n}\n\n.icon {\n fill: orange;\n stroke: #000;\n stroke-width: 2px;\n filter: drop-shadow(5px 5px 2px rgba(0, 0, 0, 0.75));\n}Run Code Online (Sandbox Code Playgroud)\r\n<h3>Resize me</h3>\n<div class="resize">\n <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">\n <path d="M 60 10 h 0 c 35.385 0 50 14.615 50 50 v 0 c 0 35.385 -14.615 50 -50 50 h 0 c -35.385 0 -50 -14.615 -50 -50 v 0 c 0 -35.385 14.615 -50 50 -50" />\n </svg>\n</div>Run Code Online (Sandbox Code Playgroud)\r\n上面的方圆是用我创建的一个简单的助手生成的:
\n codepen“方圆生成器(回旋圆角)”
如果您需要动态创建方圆(例如在绘图应用程序中),您可以将 svg<rect>与动态更新的<path>元素同步:
<path>动态更新您可以创建一个共享其宽度、高度和其他属性<path>的圆形<rect>元素的“克隆”。
矩形是隐藏的,但对变换做出响应。
\n一旦矩形被转换,<path>d 属性就会根据当前的矩形尺寸重新计算。
addClothoidPaths(2.5, 0.9, true);\n\ndocument.querySelectorAll(".inputs").forEach((input) => {\n input.addEventListener("input", (e) => {\n let r = +inputBorderRadius.value;\n let tension = +inputTension.value;\n let cubic = +document.querySelector(\'input[name="bezierType"]:checked\')\n .value;\n addClothoidPaths(r, tension, cubic);\n });\n});\n\nfunction addClothoidPaths(borderRadius = 2.5, tension = 0.9, cubic = true) {\n const ns = "http://www.w3.org/2000/svg";\n\n let rects = document.querySelectorAll("rect");\n rects.forEach((rect, i) => {\n let svg = rect.closest("svg");\n\n // create clothoid rounded path\n let rectPathGroup = svg.querySelector(".rectGroup");\n let rectPath = svg.querySelector(".rectPath" + i);\n\n if (!rectPathGroup) {\n rectPathGroup = document.createElementNS(ns, "g");\n\n rectPathGroup.classList.add("rectGroup");\n svg.append(rectPathGroup);\n\n rectPath = document.createElementNS(ns, "path");\n rectPath.classList.add("rectPath" + i);\n\n rectPathGroup.append(rectPath);\n }\n\n //console.log(rectPath)\n\n /**\n * copy rect attributes\n */\n const setAttributes = (el, attributes, exclude = []) => {\n for (key in attributes) {\n if (exclude.indexOf(key) === -1) {\n el.setAttribute(key, attributes[key]);\n }\n }\n };\n\n const getAttributes = (el) => {\n let attArr = [...el.attributes];\n let attObj = {};\n attArr.forEach(function (att) {\n attObj[att.nodeName] = att.nodeValue;\n });\n return attObj;\n };\n\n //exclude attributes not needed for paths\n let exclude = ["x", "y", "r", "rx", "ry", "height", "width", "id"];\n // copy attributes to path and set pathData\n let attributes = getAttributes(rect);\n setAttributes(rectPath, attributes, exclude);\n\n //hide rect\n rect.style.visibility = "hidden";\n\n let d = updateClothoid(rect, borderRadius, tension);\n rectPath.setAttribute("d", d);\n rectPath.style.visibility = "visible";\n\n let resizeObserver = new ResizeObserver((entries) => {\n entries.forEach((entry) => {\n let d = updateClothoid(entry.target, borderRadius, tension, cubic);\n rectPath.setAttribute("d", d);\n updateOutput();\n });\n });\n\n // Observe one or multiple elements\n resizeObserver.observe(rect);\n });\n}\n\nfunction updateOutput() {\n output.value = new XMLSerializer().serializeToString(svg);\n}\n\nfunction updateClothoid(rect, borderRadius, tension, cubic = true) {\n let x = rect.x.baseVal.value;\n let y = rect.y.baseVal.value;\n let w = rect.width.baseVal.value;\n let h = rect.height.baseVal.value;\n let r = rect.rx.baseVal.value;\n let rC = r * borderRadius;\n\n let lineHLength = w - rC * 2;\n let lineVLength = h - rC * 2;\n let d = "";\n\n // prevent border radius smaller than half width\n if (rC > w / 2 || rC > h / 2) {\n rC = Math.min(...[w, h]) / 3;\n lineHLength = w - rC * 2;\n lineVLength = h - rC * 2;\n }\n\n if (cubic) {\n d = `\n M ${x + rC} ${y}\n h ${lineHLength}\n c ${rC * tension} 0\n ${rC} ${rC * (1 - tension)} \n ${rC} ${rC} \n v ${lineVLength}\n c 0 ${rC * tension} \n -${rC * (1 - tension)} ${rC} \n -${rC} ${rC} \n h -${lineHLength}\n c -${rC * tension} 0\n -${rC} -${rC * (1 - tension)} \n -${rC} -${rC} \n v-${lineVLength}\n c 0 -${rC * tension} \n ${rC * (1 - tension)} -${rC} \n ${rC} -${rC}`;\n }\n // quadratic border smoothing\n else {\n d = `\n M ${x + rC} ${y}\n h ${lineHLength}\n q ${rC} 0\n ${rC} ${rC} \n v ${lineVLength}\n q 0 ${rC}\n -${rC} ${rC} \n h -${lineHLength}\n q -${rC} 0\n -${rC} -${rC} \n v -${lineVLength}\n q 0 -${rC}\n ${rC} -${rC}`;\n }\n\n return d.replace(/[\\n\\r\\t]/g, "").replace(/\\s{2,}/g, " ");\n}Run Code Online (Sandbox Code Playgroud)\r\n.resize {\n border: 1px solid #ccc;\n resize: both;\n overflow: auto;\n width: 50%;\n max-width: 50%;\n}\n\nsvg {\n display: block;\n width: 100%;\n height: 100%;\n}\n\ntextarea {\n width: 100%;\n min-height: 20em;\n}Run Code Online (Sandbox Code Playgroud)\r\n<p>Border-radius<input class="inputs" id="inputBorderRadius" type="range" min="1" max="5" step="0.1"></p>\n<p>Tension (only for cubic)<input class="inputs" id="inputTension" type="range" min="0.5" max="1" step="0.1"></p>\n\n<p><label> <input class="inputs" type="radio" name="bezierType" value="1" checked> Cubic</label> <label> <input class="inputs" type="radio" name="bezierType" value="0"> Quadratic</label> </p>\n\n\n<h3>Resize me</h3>\n<div class="resize">\n <svg id="svg">\n <rect id="rect" x="10%" y="10%" width="80%" height="80%" rx="10" fill="#ccc" stroke="#000" stroke-width="10" transform="rotate(0)" transform-orgin="center" />\n </svg>\n</div>\n\n<fieldset>\n <legend>Output</legend>\n <textarea id="output"></textarea>\n</fieldset>Run Code Online (Sandbox Code Playgroud)\r\n基于 rect 初始rx边界半径属性,我们可以计算二次或三次曲线段,如下所示:
function updateClothoid(rect, borderRadius, tension, cubic = true) {\n let x = rect.x.baseVal.value;\n let y = rect.y.baseVal.value;\n let w = rect.width.baseVal.value;\n let h = rect.height.baseVal.value;\n let r = rect.rx.baseVal.value;\n let rC = r * borderRadius;\n\n // horizontal and vertical line segments between curves\n let lineHLength = w - rC * 2;\n let lineVLength = h - rC * 2;\n let d = "";\n\n // prevent border radius smaller than half width\n if (rC > w / 2 || rC > h / 2) {\n rC = Math.min(...[w, h]) / 3;\n lineHLength = w - rC * 2;\n lineVLength = h - rC * 2;\n }\n\n if (cubic) {\n d = `\n M ${x + rC} ${y}\n h ${lineHLength}\n c ${rC * tension} 0\n ${rC} ${rC * (1 - tension)} \n ${rC} ${rC} \n v ${lineVLength}\n c 0 ${rC * tension} \n -${rC * (1 - tension)} ${rC} \n -${rC} ${rC} \n h -${lineHLength}\n c -${rC * tension} 0\n -${rC} -${rC * (1 - tension)} \n -${rC} -${rC} \n v-${lineVLength}\n c 0 -${rC * tension} \n ${rC * (1 - tension)} -${rC} \n ${rC} -${rC}`;\n }\n // quadratic border smoothing\n else {\n d = `\n M ${x + rC} ${y}\n h ${lineHLength}\n q ${rC} 0\n ${rC} ${rC} \n v ${lineVLength}\n q 0 ${rC}\n -${rC} ${rC} \n h -${lineHLength}\n q -${rC} 0\n -${rC} -${rC} \n v -${lineVLength}\n q 0 -${rC}\n ${rC} -${rC}`;\n }\n\n // remove whitespace\n return d.replace(/[\\n\\r\\t]/g, "").replace(/\\s{2,}/g, " ");\n}\nRun Code Online (Sandbox Code Playgroud)\n较高rC将增加初始边界半径以获得更平滑的曲线。
\n三次 B\xc3\xa9ziers 可以更好地控制曲率。
较高的tension值会将控制点“拉”到角落,从而导致视觉上较小的边框半径。
与基于添加圆弧的默认边界半径方法相比,这两个选项(二次和三次)都会在直线和曲线之间产生更平滑的过渡。
\nfunction updateClothoid(rect, borderRadius, tension, cubic = true) {\n let x = rect.x.baseVal.value;\n let y = rect.y.baseVal.value;\n let w = rect.width.baseVal.value;\n let h = rect.height.baseVal.value;\n let r = rect.rx.baseVal.value;\n let rC = r * borderRadius;\n\n // horizontal and vertical line segments between curves\n let lineHLength = w - rC * 2;\n let lineVLength = h - rC * 2;\n let d = "";\n\n // prevent border radius smaller than half width\n if (rC > w / 2 || rC > h / 2) {\n rC = Math.min(...[w, h]) / 3;\n lineHLength = w - rC * 2;\n lineVLength = h - rC * 2;\n }\n\n if (cubic) {\n d = `\n M ${x + rC} ${y}\n h ${lineHLength}\n c ${rC * tension} 0\n ${rC} ${rC * (1 - tension)} \n ${rC} ${rC} \n v ${lineVLength}\n c 0 ${rC * tension} \n -${rC * (1 - tension)} ${rC} \n -${rC} ${rC} \n h -${lineHLength}\n c -${rC * tension} 0\n -${rC} -${rC * (1 - tension)} \n -${rC} -${rC} \n v-${lineVLength}\n c 0 -${rC * tension} \n ${rC * (1 - tension)} -${rC} \n ${rC} -${rC}`;\n }\n // quadratic border smoothing\n else {\n d = `\n M ${x + rC} ${y}\n h ${lineHLength}\n q ${rC} 0\n ${rC} ${rC} \n v ${lineVLength}\n q 0 ${rC}\n -${rC} ${rC} \n h -${lineHLength}\n q -${rC} 0\n -${rC} -${rC} \n v -${lineVLength}\n q 0 -${rC}\n ${rC} -${rC}`;\n }\n\n // remove whitespace\n return d.replace(/[\\n\\r\\t]/g, "").replace(/\\s{2,}/g, " ");\n}\nRun Code Online (Sandbox Code Playgroud)\r\nlet d = updateClothoid(rect, 2.5, 0.9, true);\npath.setAttribute(\'d\', d)\n\nfunction updateClothoid(rect, borderRadius, tension, cubic = true) {\n let x = rect.x.baseVal.value;\n let y = rect.y.baseVal.value;\n let w = rect.width.baseVal.value;\n let h = rect.height.baseVal.value;\n let r = rect.rx.baseVal.value;\n let rC = r * borderRadius;\n\n // horizontal and vertical line segments between curves\n let lineHLength = w - rC * 2;\n let lineVLength = h - rC * 2;\n let d = "";\n\n // prevent border radius smaller than half width\n if (rC > w / 2 || rC > h / 2) {\n rC = Math.min(...[w, h]) / 3;\n lineHLength = w - rC * 2;\n lineVLength = h - rC * 2;\n }\n\n if (cubic) {\n d = `\n M ${x + rC} ${y}\n h ${lineHLength}\n c ${rC * tension} 0\n ${rC} ${rC * (1 - tension)} \n ${rC} ${rC} \n v ${lineVLength}\n c 0 ${rC * tension} \n -${rC * (1 - tension)} ${rC} \n -${rC} ${rC} \n h -${lineHLength}\n c -${rC * tension} 0\n -${rC} -${rC * (1 - tension)} \n -${rC} -${rC} \n v-${lineVLength}\n c 0 -${rC * tension} \n ${rC * (1 - tension)} -${rC} \n ${rC} -${rC}`;\n }\n // quadratic border smoothing\n else {\n d = `\n M ${x + rC} ${y}\n h ${lineHLength}\n q ${rC} 0\n ${rC} ${rC} \n v ${lineVLength}\n q 0 ${rC}\n -${rC} ${rC} \n h -${lineHLength}\n q -${rC} 0\n -${rC} -${rC} \n v -${lineVLength}\n q 0 -${rC}\n ${rC} -${rC}`;\n }\n\n // remove whitespace\n return d.replace(/[\\n\\r\\t]/g, "").replace(/\\s{2,}/g, " ");\n}Run Code Online (Sandbox Code Playgroud)\r\nsvg {\n display: block;\n width: 20em;\n border: 1px solid #ccc\n}Run Code Online (Sandbox Code Playgroud)\r\n您还可以使用 CSS 方圆生成器,例如“CSS Clothoid Corners”。
\n<svg id="svg" viewBox="0 0 100 100">\n <rect id="rect" x="10%" y="10%" width="80%" height="80%" rx="10" fill="none" stroke="#ccc" stroke-width="0.5" transform="rotate(0)" transform-orgin="center" />\n <path id="path" fill="none" stroke="red" stroke-width="0.75"/>\n </svg>Run Code Online (Sandbox Code Playgroud)\r\n:root{\n --clip: polygon(45.837405% 0%,\n calc(100% - 45.837405%) 0%,\n calc(100% - 41.024763%) 0.022716%,\n calc(100% - 36.21469%) 0.166797%,\n calc(100% - 31.418198%) 0.543282%,\n calc(100% - 26.661607%) 1.261583%,\n calc(100% - 22.002771%) 2.456312%,\n calc(100% - 17.530819%) 4.217852%,\n calc(100% - 13.363987%) 6.605826%,\n calc(100% - 9.657177%) 9.657177%,\n calc(100% - 6.605826%) 13.363987%,\n calc(100% - 4.217852%) 17.530819%,\n calc(100% - 2.456312%) 22.002771%,\n calc(100% - 1.261583%) 26.661607%,\n calc(100% - 0.543282%) 31.418198%,\n calc(100% - 0.166797%) 36.21469%,\n calc(100% - 0.022716%) 41.024763%,\n calc(100% - 0.022716%) calc(100% - 41.024763%),\n calc(100% - 0.166797%) calc(100% - 36.21469%),\n calc(100% - 0.543282%) calc(100% - 31.418198%),\n calc(100% - 1.261583%) calc(100% - 26.661607%),\n calc(100% - 2.456312%) calc(100% - 22.002771%),\n calc(100% - 4.217852%) calc(100% - 17.530819%),\n calc(100% - 6.605826%) calc(100% - 13.363987%),\n calc(100% - 9.657177%) calc(100% - 9.657177%),\n calc(100% - 13.363987%) calc(100% - 6.605826%),\n calc(100% - 17.530819%) calc(100% - 4.217852%),\n calc(100% - 22.002771%) calc(100% - 2.456312%),\n calc(100% - 26.661607%) calc(100% - 1.261583%),\n calc(100% - 31.418198%) calc(100% - 0.543282%),\n calc(100% - 36.21469%) calc(100% - 0.166797%),\n calc(100% - 41.024763%) calc(100% - 0.022716%),\n calc(100% - 45.837405%) 100%,\n 45.837405% 100%,\n 41.024763% calc(100% - 0.022716%),\n 36.21469% calc(100% - 0.166797%),\n 31.418198% calc(100% - 0.543282%),\n 26.661607% calc(100% - 1.261583%),\n 22.002771% calc(100% - 2.456312%),\n 17.530819% calc(100% - 4.217852%),\n 13.363987% calc(100% - 6.605826%),\n 9.657177% calc(100% - 9.657177%),\n 6.605826% calc(100% - 13.363987%),\n 4.217852% calc(100% - 17.530819%),\n 2.456312% calc(100% - 22.002771%),\n 1.261583% calc(100% - 26.661607%),\n 0.543282% calc(100% - 31.418198%),\n 0.166797% calc(100% - 36.21469%),\n 0.022716% calc(100% - 41.024763%),\n 0.022716% 41.024763%,\n 0.166797% 36.21469%,\n 0.543282% 31.418198%,\n 1.261583% 26.661607%,\n 2.456312% 22.002771%,\n 4.217852% 17.530819%,\n 6.605826% 13.363987%,\n 9.657177% 9.657177%,\n 13.363987% 6.605826%,\n 17.530819% 4.217852%,\n 22.002771% 2.456312%,\n 26.661607% 1.261583%,\n 31.418198% 0.543282%,\n 36.21469% 0.166797%,\n 41.024763% 0.022716%,\n 45.837405% 0%);\n}\n\n\n\n.cloth-wrp{\n position: relative;\n display: inline-block;\n width:50%;\n padding: 5px;\n filter: drop-shadow(5px 5px 5px rgba(0,0,0,0.5));\n}\n\n.cloth-wrp:before{\n content:\'\';\n display:block;\n position:absolute;\n top:0;\n left:0;\n right:0;\n bottom:0;\n width:100%;\n height:100%;\n background:#000;\n clip-path: var(--clip)\n}\n\n.clothoid-corner {\n display: flex;\n align-items: center;\n justify-content: center;\n background-color:orange;\n width:100%;\n aspect-ratio: 1/1;\n}\n\n.clipped{\n clip-path: var(--clip)\n}Run Code Online (Sandbox Code Playgroud)\r\n该剪辑路径实际上是多边形近似。
\n我们需要将方圆包裹在相对定位的父 div 中。
\n此包装器引入了一个使用相同剪辑路径剪辑的伪元素 \xe2\x80\x93。
\n伪元素具有背景颜色,这将导致最终的伪描边颜色。
\n笔划宽度由应用于环绕元素的填充来定义。
| 归档时间: |
|
| 查看次数: |
1199 次 |
| 最近记录: |