CSS 绘制扇面
参考资料:https://juejin.cn/post/7266641059282927650
效果:
源码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>使用css3绘制任意角度扇形</title> <style> .sector { position: relative; display: inline-block; width: 80px; height: 80px; background-color: transparent; border-radius: 50%; overflow: hidden; transform: rotate(45deg); } .sector::before { display: inline-block; content: ''; width: 100%; height: 100%; background: linear-gradient(90deg, #4D84FF 25%,#eee 0, #eee 50%,#4D84FF 0, #4D84FF 75%,#eee 0); background-size: 30px; clip-path: polygon(50% 50%, 0 0, 100% 0); opacity: .7; } .sector::after { position: absolute; display: inline-block; content: ''; width: 80px; height: 80px; top: calc(50% - 40px); left: calc(50% - 40px); border-radius: 50%; background-color: #fff; z-index: 1; } </style> </head> <body> <div class="sector"></div> </body> <script> /** * @param {Number} radius: 外半径 * @param {Number} minRadius: 内半径 * @param {Number} angles: 角度 * @param {Number} direct: 方向 **/ function drawPie({radius, minRadius, angles, direct, selector}) { let points = ['50% 50%', '0 0'], residue = angles%45? angles%45:45, percent = 0 direct = direct || 45; angles > 90 && points.push('100% 0'); angles > 180 && points.push('100% 100%'); angles > 270 && points.push('0 100%'); //let percent = (100/2) * (Math.tan(2*Math.PI/360 * residue).toFixed(4)); // tan算出来的是相对半径的占比 percent = (100/2) * (Math.tan(2*Math.PI/360 * residue).toFixed(4)); if(angles<=45) { points.push(percent + '%' + ' 0'); }else if(angles<=90) { points.push(percent + 50 + '%' + ' 0'); }else if(angles<=135) { points.push('100% ' + percent + '%'); }else if(angles<=180) { points.push('100% ' + (percent + 50) + '%'); }else if(angles<=225) { points.push(100 - percent + '%' + ' 100%'); }else if(angles<=270) { points.push(50 - percent + '%' + ' 100%'); }else if(angles<=315) { points.push('0 ' + (100 - percent) + '%'); }else if(angles<=360) { points.push('0 ' + (50 - percent) + '%'); } let path = 'polygon(' + points.join(', ') + ')'; // 外扇面 addCSSRule(selector + '::before', { 'clip-path': path }); // 内扇面 addCSSRule(selector + '::after', { 'clip-path': path, width: minRadius + 'px', height: minRadius + 'px', top: 'calc(50% - ' + minRadius/2 + 'px)', left: 'calc(50% - ' + minRadius/2 + 'px)' }); // 容器大小和方向 addCSSRule(selector, { width: radius + 'px', height: radius + 'px', transform: 'rotate(' + direct + 'deg)' }); } // 绘制扇面 drawPie({ selector: '.sector', radius: 400, // 外半径 minRadius: 180, // 内半径 angles: 120, // 弧度 direct: 100 // 方向 }); function addCSSRule(selector, rules, index) { // 创建一个style元素 var style = document.createElement('style'); // 设置type属性为text/css style.type = 'text/css'; // 插入到head中 document.head.appendChild(style); // 获取sheet var sheet = style.sheet; // 如果index未提供,则添加到末尾 index = index || null; // 如果是CSS规则字符串 if (typeof rules === 'string') { // 直接添加 sheet.insertRule(selector + ' {' + rules + '}', index); } else { // 如果是一个对象 // 遍历对象中的所有属性 for (var prop in rules) { if (rules.hasOwnProperty(prop)) { // 将属性和值转换为字符串 var rule = prop + ': ' + rules[prop]; // 添加到样式表中 sheet.insertRule(selector + ' {' + rule + '}', index); } } } } /* addCSSRule('.my-other-class', 'color: red; background-color: yellow;', 0); // 添加到顶部 */ </script> </html>
进阶联动版
代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>使用css3绘制任意角度扇形</title> <style> html,body { height: 98%; } body { background: url(https://t11.baidu.com/it/u=2644047138,236799825&fm=30&app=106&f=JPEG?w=640&h=439&s=C250CF3093F3C1CE5670B8DE0300D0B1) center center fixed no-repeat; } .sector-ctn { position: absolute; top: 45%; left: 40%; } .sector-ctn.expand { display: block; } .cell-sector-ctn { position: absolute; top: 50%; left: 50%; } .cell-sector-ctn::before { top: -5.5px; left: -5.5px; display: inline-block; position: absolute; content: ''; width: 11px; height: 11px; border-radius: 11px; background: linear-gradient(180deg, #4F96FE 0%, #2779F5 100%); z-index: 100; } /* 扇面 */ .sector, .cell-sector { position: absolute; display: inline-block; background-color: transparent; border-radius: 50%; overflow: hidden; } .sector::before, .cell-sector::before { display: inline-block; content: ''; width: 100%; height: 100%; background-image: radial-gradient( transparent 0%, rgb(77 132 255 / 10%) 10px, rgb(77 132 255 / 40%) 20px, rgb(77 132 255 / 100%) 36px, rgb(77 132 255 / 95%) 15%, rgb(77 132 255 / 90%) 20%, rgb(77 132 255 / 85%) 30%, rgb(77 132 255 / 75%) 40%, rgb(77 132 255 / 60%) 50%, rgb(77 132 255 / 56%) 52%, rgb(115 195 255 / 80%) 61%, rgb(249 249 249 / 50%) 80%, rgb(249 249 249 / 0.1%) 95%, transparent 100% ); opacity: .8; } .sector::after, .cell-sector::after { position: absolute; display: inline-block; content: ''; border-radius: 50%; background-color: rgb(255 255 255 / 65%); z-index: 1; } .sector { display: none; } .sector.expand { display: inline-block; } .cell-sector::before { background-image: radial-gradient( #fff 0%, #fff 98%, rgb(77 132 255 / 100%) 100% ); opacity: 1; } .cell-sector::after { background-color: rgb(0 0 0 / 15%); } .cell-sector.select::before { background-image: radial-gradient( rgb(77 132 255 / 50%) 0%, rgb(77 132 255 / 80%) 40%, rgb(77 132 255 / 100%) 100% ); opacity: .8; } .cell-sector.expand::before, .cell-sector.expand::after { background-image: none; background-color: transparent; } .hover-square { position: absolute; top: -12.5px !important; left: -12.5px !important; width: 25px !important; height: 25px !important; background-color:aqua; z-index: 1000; opacity: .1; border-radius: 0 0 25px 0; } </style> </head> <body> <div class="sector-ctn"> <div class="sector"></div> <div class="cell-sector-ctn"> <div class="cell-sector cell-1"></div> <div class="cell-sector cell-2"></div> <div class="cell-sector cell-3"></div> <div onclick="cellClick(this, event)" cell="1" class="hover-square cell-1"></div> <div onclick="cellClick(this, event)" cell="2" class="hover-square cell-2"></div> <div onclick="cellClick(this, event)" cell="3" class="hover-square cell-3"></div> </div> </div> </body> <script> let direct = 1; let cellOptions = { radius: 36, minRadius: 15, angles: 115, direct: direct, translate: 'translate(12.5px, 12.5px)' }; let base = { selector: '.sector', radius: 160, // 外半径 minRadius: 60, // 内半径 angles: 115, // 弧度 direct: direct, // 方向 }; document.body.removeEventListener('click', blurCell); document.body.addEventListener('click', blurCell); function blurCell(evt) { console.log('outer click') document.querySelector('.sector').classList.remove('expand'); document.querySelectorAll('.cell-sector').forEach((item)=>{ item.classList.remove('expand'); }); } function cellClick(dom, evt) { let target = evt.target, cellIndex = target.getAttribute('cell'); console.log(target, cellIndex) // 动态绘制小区信号扇面 drawPie({ selector: base.selector, radius: base.radius, // 外半径 minRadius: base.minRadius, // 内半径 angles: base.angles, // 弧度 direct: base.direct + (cellIndex - 1)*120 // 方向 }); // 添加expand类,展示信号区域 document.querySelectorAll('.cell-sector').forEach((item)=>{ item.classList.remove('expand'); item.classList.remove('select'); }); let curCell = document.querySelector('.cell-sector.cell-'+cellIndex); curCell.classList.add('expand'); curCell.classList.add('select'); document.querySelector('.sector').classList.add('expand'); evt.stopPropagation(); } /** * @param {Number} radius: 外半径 * @param {Number} minRadius: 内半径 * @param {Number} angles: 角度 * @param {Number} direct: 方向 **/ function drawPie({radius, minRadius, angles, direct, selector}) { let points = ['50% 50%', '0 0'], residue = angles%45? angles%45:45, percent = 0 direct = direct || 45; angles > 90 && points.push('100% 0'); angles > 180 && points.push('100% 100%'); angles > 270 && points.push('0 100%'); //let percent = (100/2) * (Math.tan(2*Math.PI/360 * residue).toFixed(4)); // tan算出来的是相对半径的占比 percent = (100/2) * (Math.tan(2*Math.PI/360 * residue).toFixed(4)); if(angles<=45) { points.push(percent + '%' + ' 0'); }else if(angles<=90) { points.push(percent + 50 + '%' + ' 0'); }else if(angles<=135) { points.push('100% ' + percent + '%'); }else if(angles<=180) { points.push('100% ' + (percent + 50) + '%'); }else if(angles<=225) { points.push(100 - percent + '%' + ' 100%'); }else if(angles<=270) { points.push(50 - percent + '%' + ' 100%'); }else if(angles<=315) { points.push('0 ' + (100 - percent) + '%'); }else if(angles<=360) { points.push('0 ' + (50 - percent) + '%'); } let path = 'polygon(' + points.join(', ') + ')', zoom = 5; try{ zoom = globMap.getZoom(); }catch(e){} let rateRels = { 0: 0.10, 1: 0.10, 2: 0.10, 3: 0.10, 4: 0.13, 5: 0.25, 6: 0.45, 7: 0.75, 8: 1.3, 9: 2, 10: 2, 11: 3, 12: 3, 13: 5, 14: 5, 15: 8 }, rate = rateRels[zoom]*zoom; console.log('zoom: ', zoom, rate) // 外扇面 addCSSRule(selector + '::before', { 'clip-path': path }); // 内扇面 addCSSRule(selector + '::after', { 'clip-path': path, width: minRadius*rate + 'px', height: minRadius*rate + 'px', top: 'calc(50% - ' + minRadius*rate/2 + 'px)', left: 'calc(50% - ' + minRadius*rate/2 + 'px)' }); // 容器大小和方向 addCSSRule(selector, { top: '-' + radius*rate/2 +'px', left: '-' + radius*rate/2 +'px', width: radius*rate + 'px', height: radius*rate + 'px', transform: 'rotate(' + direct + 'deg)' }); } // 绘制扇面 /* drawPie({ selector: '.sector', radius: 160, // 外半径 minRadius: 60, // 内半径 angles: angles, // 弧度 direct: direct // 方向 }); */ // cell-1 drawPie({ selector: '.cell-sector-ctn .cell-1', radius: cellOptions.radius, // 外半径 minRadius: cellOptions.minRadius, // 内半径 angles: cellOptions.angles, // 弧度 direct: cellOptions.direct, // 方向 translate: cellOptions.translate }); // cell-2 drawPie({ selector: '.cell-sector-ctn .cell-2', radius: cellOptions.radius, // 外半径 minRadius: cellOptions.minRadius, // 内半径 angles: cellOptions.angles, // 弧度 direct: cellOptions.direct + 120, // 方向 translate: cellOptions.translate }); // cell-3 drawPie({ selector: '.cell-sector-ctn .cell-3', radius: cellOptions.radius, // 外半径 minRadius: cellOptions.minRadius, // 内半径 angles: cellOptions.angles, // 弧度 direct: cellOptions.direct + 240, // 方向 translate: cellOptions.translate }); let squareDirect = direct - 127, squareTranslate = cellOptions.translate; // 容器大小和方向 addCSSRule('.hover-square.cell-1', { transform: 'rotate(' + squareDirect + 'deg)' + ' ' + squareTranslate + ' !important' }); // 容器大小和方向 addCSSRule('.hover-square.cell-2', { transform: 'rotate(' + (squareDirect + 120) + 'deg)' + ' ' + squareTranslate + ' !important' }); // 容器大小和方向 addCSSRule('.hover-square.cell-3', { transform: 'rotate(' + (squareDirect + 240) + 'deg)' + ' ' + squareTranslate + ' !important' }); function addCSSRule(selector, rules, index) { // 创建一个style元素 var style = document.createElement('style'); // 设置type属性为text/css style.type = 'text/css'; // 插入到head中 document.head.appendChild(style); // 获取sheet var sheet = style.sheet; // 如果index未提供,则添加到末尾 index = index || null; // 如果是CSS规则字符串 if (typeof rules === 'string') { // 直接添加 sheet.insertRule(selector + ' {' + rules + '}', index); } else { // 如果是一个对象 // 遍历对象中的所有属性 for (var prop in rules) { if (rules.hasOwnProperty(prop)) { // 将属性和值转换为字符串 var rule = prop + ': ' + rules[prop]; // 添加到样式表中 sheet.insertRule(selector + ' {' + rule + '}', index); } } } } /* addCSSRule('.my-other-class', 'color: red; background-color: yellow;', 0); // 添加到顶部 */ </script> </html>