这几天有感于一个项目里的雷达扫描效果太差,不堪入目,于是自己尝试用css实现了一个类似的效果,现在贴出来,跟大家一起学习进步。
首先是封装过的前端雷达插件radar.js:
window.Radar = function (parent, cfg) {
var radar = {
parent: $(parent),
defaultConfig: {
frequency: 1, // 雷达波基础频率
radius: '500px', // 默认扫描半径
centerPoint: {x: '500px', y: '500px'}, // 默认中心坐标
scanColor: '#00ff00', // 默认扫描效果颜色
senterColor: '#00ff00' // 默认中心雷达效果颜色
waveNum: 6, // 默认辐射波纹密度
waveLlineWidth: '3px', // 默认辐射波纹线宽
waveLineStyle: 'solid', // 默认辐射波纹线型
waveLineColor: '#00ff00', // 默认辐射波纹线颜色
},
config: {},
flyPoint: null,
dom: null,
isScan: false,
scanInterval: null,
hexToRgba: function(hex = '#000000', alpha = 1) {
hex = hex.replace(/^#/, '');
alpha = alpha > 1 || alpha < 0 ? 1 : alpha;
if (hex.length === 3) {
hex = hex.split('').map(char => char + char).join('');
}
if (!/^[0-9A-F]{6}$/i.test(hex)) {
throw new Error('Invalid hex color format');
}
const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16);
return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')';
},
isScanOver: function(point, lineStart, lineEnd) {
const {left: px, top: py} = point;
const {left: x1, top: y1} = lineStart;
const {left: x2, top: y2} = lineEnd;
if ((x1 < x2 && (px < x1 || px > x2))
|| (y1 < y2 && (py < y1 || py > y2))
|| (x1 > x2 && (px > x1 || px < x2))
|| (y1 > y2 && (py > y1 || py < y2))) {
return false;
}
const A = px - x1;
const B = py - y1;
const C = x2 - x1;
const D = y2 - y1;
const dot = A * C + B * D;
const lenSq = C * C + D * D;
let param = -1;
if (lenSq !== 0) {
param = dot / lenSq;
}
let xx, yy;
if (param < 0) {
xx = x1;
yy = y1;
} else if (param > 1) {
xx = x2;
yy = y2;
} else {
xx = x1 + param * C;
yy = y1 + param * D;
}
const dx = px - xx;
const dy = py - yy;
return Math.sqrt(dx * dx + dy * dy) < 15;
},
setStyle: function() {
var th = this;
var cf = th.config;
var css = ' <style type="text/css">'
+ ' /* 雷达扫描区域 */'
+ ' .radar-container {'
+ ' position: relative;'
+ ' background-color: transparent;'
+ ' overflow: hidden;'
+ ' width: ' + th.radius * 2 + 'px;'
+ ' height: ' + th.radius * 2 + 'px;'
+ ' padding: 0;'
+ ' margin: 0;'
+ ' top: ' + cf.top + 'px;'
+ ' left: ' + cf.left + 'px;'
+ ' border-radius: 50%;'
+ ' }'
+ ' /* 雷达原点 */'
+ ' .radar-center {'
+ ' position: absolute;'
+ ' top: ' + th.radius + 'px;'
+ ' left: ' + th.radius + 'px;'
+ ' transform: translate(-50%, -50%);'
+ ' width: 20px;'
+ ' height: 20px;'
+ ' background: radial-gradient(circle, ' + th.hexToRgba(cf.senterColor, 1) + ' 0%, ' + th.hexToRgba(cf.senterColor, 0.8) + ' 50%, ' + th.hexToRgba(cf.senterColor, 0.3) + ' 100%);'
+ ' border-radius: 50%;'
+ ' z-index: 10;'
+ ' animation: radar-pulse ' + (cf.frequency || 1) + 's ease-in-out infinite;'
+ ' }'
+ ' '
+ ' /* 动态扩散波纹 */'
+ ' .radar-wave {'
+ ' position: absolute;'
+ ' top: ' + th.radius + 'px;'
+ ' left: ' + th.radius + 'px;'
+ ' transform: translate(-50%, -50%);'
+ ' width: 20px;'
+ ' height: 20px;'
+ ' border: ' + cf.waveLlineWidth + ' ' + cf.waveLineStyle + ' ' + th.hexToRgba(cf.waveLineColor, 0.9) + ';'
+ ' border-radius: 50%;'
+ ' opacity: 0;'
+ ' animation: radar-wave ' + cf.waveNum + 's linear infinite;'
+ ' }'
+ ' '
+ ' /* 扫描扇区 */'
+ ' .radar-sector {'
+ ' position: relative;'
+ ' top: 0px;'
+ ' left: ' + th.radius + 'px;'
+ ' width: ' + th.radius + 'px;'
+ ' height: ' + th.radius * 2 + 'px;'
+ ' transform-origin: 0% 50%;'
+ ' background: conic-gradient('
+ ' from 0deg at 0% 50%,'
+ ' transparent 0deg,'
+ ' ' + th.hexToRgba(cf.scanColor, 0.0) + ' 0deg,'
+ ' ' + th.hexToRgba(cf.scanColor, 0.4) + ' 180deg,'
+ ' transparent 180deg'
+ ' );'
+ ' animation: radar-sector ' + (cf.frequency || 1) * 6 + 's linear infinite;'
+ ' z-index: 5;'
+ ' }'
+ ' /* 扫描线顶点坐标 */'
+ ' .radar-scan-top {'
+ ' position: relative;'
+ ' height: 1px;'
+ ' width: 1px;'
+ ' top:100%;'
+ ' left:0%;'
+ ' }'
+ ' /* 雷达原点缩放效果 */'
+ ' @keyframes radar-pulse {'
+ ' 0%, 100% {'
+ ' box-shadow: 0 0 0 0 ' + th.hexToRgba(cf.senterColor, 0.4) + ';'
+ ' transform: translate(-50%, -50%) scale(1);'
+ ' }'
+ ' 50% {'
+ ' box-shadow: 0 0 0 20px ' + th.hexToRgba(cf.senterColor, 0) + ';'
+ ' transform: translate(-50%, -50%) scale(1.1);'
+ ' }'
+ ' }'
+ ' '
+ ' /* 波纹效果 */'
+ ' @keyframes radar-wave {'
+ ' 0% {'
+ ' width: 20px;'
+ ' height: 20px;'
+ ' opacity: 0.8;'
+ ' }'
+ ' 100% {'
+ ' width: ' + th.radius * 2 + 'px;'
+ ' height: ' + th.radius * 2 + 'px;'
+ ' opacity: 0;'
+ ' }'
+ ' }'
+ ' /* 扫描效果 */'
+ ' @keyframes radar-sector {'
+ ' 0% {'
+ ' transform: rotate(0deg);'
+ ' }'
+ ' 100% {'
+ ' transform: rotate(360deg);'
+ ' }'
+ ' }'
+ ' /* 飞行点样式 */'
+ ' .fly-point {'
+ ' position: absolute;'
+ ' width: 20px;'
+ ' height: 20px;'
+ ' border-radius: 50%;'
+ ' }'
+ ' /* 飞行点渐变效果 */'
+ ' .fly-point.fade-out {'
+ ' background-color: rgba(255, 255, 0, 0.1) !important;'
+ ' transition: background-color 3s ease;'
+ ' }'
+ 'tyle>';
$('head').append(css);
},
/* 扫描飞行点 */
scan: function(){
var th = this;
var ctft = th.centerPointDom.offset();
var txt = $('.flyPoint');
th.scanInterval = setInterval(function(){
if(th.flyPoint){
var dom = th.flyPoint.dom;
if(dom){
var ptft = dom.offset();
var ltft = th.scanTopPointDom.offset();
if(!th.isScan){
if(th.isScanOver(ptft, ctft, ltft)){
th.isScan = true;
dom.css({backgroundColor: 'rgba(255, 255, 0, 0.8) '});
setTimeout(function(){
dom[0].classList.add('fade-out');
}, 200);
setTimeout(function(){
dom.css({backgroundColor: 'rgba(255, 255, 0, 0.1) '});
dom[0].classList.remove('fade-out');
th.isScan = false;
}, 3000);
}
}
}
}
}, 1);
},
/* 随机生成一个飞行点
本方法是为了产生一个从任意角度飞入的圆点,暂时没有想到好的算法,因此代码比较臃肿,
希望大家能给出一个较好的算法实现。
*/
randomFlyPoint: function(){
var th = this;
var cfg = th.config;
var r1 = Math.random() * 4;
var rdm1 = parseInt(Math.random() * 2 * th.radius, 10);
var rdm2 = parseInt(Math.random() * (3 * th.radius - 200), 10) + 100;
var points = {};
if(r1 <= 1){
points.start = {x: cfg.left, y: cfg.top + rdm1};
points.end = rdm2 <= th.radius ? {x: cfg.left + rdm2, y: cfg.top}
: rdm2 <= 2 * th.radius ? {x: cfg.left + 2 * th.radius, y: cfg.top + rdm2 - th.radius}
: {x: cfg.left + rdm2 - 2 * th.radius + 100, y: cfg.top + 2 * th.radius};
}else if(r1 <= 2){
points.start = {x: cfg.left + rdm1, y: cfg.top};
points.end = rdm2 <= th.radius ? {x: cfg.left + 2 * th.radius, y: cfg.top + rdm2 - th.radius}
: rdm2 <= 2 * th.radius ? {x: cfg.left + rdm2 - th.radius, y: cfg.top + 2 * th.radius}
: {x: cfg.left, y: cfg.top + rdm2 - 2 * th.radius + 100};
}else if(r1 <= 3){
points.start = {x: cfg.left + 2 * th.radius, y: cfg.top + rdm1};
points.end = rdm2 <= th.radius ? {x: cfg.left + rdm2 - 100, y: cfg.top + 2 * th.radius}
: rdm2 <= 2 * th.radius ? {x: cfg.left, y: cfg.top + rdm2 - th.radius}
: {x: cfg.left + rdm2 - 2 * th.radius - 100, y: cfg.top};
}else{
points.start = {x: cfg.left + rdm1, y: cfg.top + 2 * th.radius};
points.end = rdm2 <= th.radius ? {x: cfg.left, y: cfg.top + rdm2 - 100}
: rdm2 <= 2 * th.radius ? {x: cfg.left + rdm2 - th.radius, y: cfg.top}
: {x: cfg.left + 2 * th.radius, y: cfg.top + rdm2 - 2 * th.radius};
}
return {startPoint: points.start, endPoint: points.end, distince: {x: points.end.x - points.start.x, y: points.end.y - points.start.y}, time: 100, isScan: false};
},
/* 画面添加一个飞行点 */
addFlyPoint: function(){
var th = this;
var flyPoint = th.randomFlyPoint();
if(th.flyPoint){
th.flyPoint.dom.remove();
th.flyPoint = null;
}
var dm = flyPoint.dom = $('<div class="fly-point" style="left:' + flyPoint.startPoint.x + 'px;top:' + flyPoint.startPoint.y + 'px;"></div>').appendTo(th.dom);
th.flyPoint = flyPoint;
dm.animate({
top: flyPoint.endPoint.y,
left: flyPoint.endPoint.x
}, flyPoint.time * 1000, 'linear', function(){
dm.remove();
th.flyPoint = null;
});
},
/**
* 实例化雷达
*/
init: function(cfg){
var th = this;
// 合并配置
th.config = Object.assign({}, th.defaultConfig, cfg);
var cf = th.config;
th.radius = parseInt(cf.radius, 10);
// 计算雷达扫描区域起始点坐标
cf.top = parseInt(cf.centerPoint.y, 10) - th.radius;
cf.left = parseInt(cf.centerPoint.x, 10) - th.radius;
// 添加样式
var cssStr = th.setStyle();
$('head').append(cssStr);
// 初始化DOM
var htm = '<div class="radar-container">';
for(var i = 0; i < cf.waveNum; i++){
htm += ' <div class="radar-wave" style="animation-delay: ' + i + 's;"></div>';
}
htm += ''
+ ' <div class="radar-sector"><div class="radar-scan-top"></div></div>'
+ ' <div class="radar-center"></div>'
+ '</div>';
th.dom = $(htm).appendTo(th.parent);
th.centerPointDom = $('.radar-center');
th.scanTopPointDom = $('.radar-scan-top');
}
}
radar.init(cfg);
return radar;
};
$.fn.addRadar = function (cfg){
return new Radar(this, cfg);
}
接着就是来个网页,调用该radar:
<!DOCTYPE html>
<html style="font-size: 20px;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>雷达扫描效果</title>
<link href="../img/favicon.ico" rel="icon" type="image/x-icon">
<link href="../css/site.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="../js/modernizr-2.6.2.js"></script>
<script type="text/javascript" src="../js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="../js/radar.js"></script>
<style type="text/css">
.addFlyBtn{
position: absolute; width: 160px;height: 40px;line-height: 40px;top: 60px; left:1500px; font-size: 16px; font-weight: bold; border-radius: 5px; text-align: center; background-color: rgba(30, 60, 140, 0.6); cursor: pointer;
}
</style>
</head>
<body style="font-size: 20px;">
<div class="body" style="background: url(../img/map.png) no-repeat 0 0; background-size: cover;"></div>
<div class="addFlyBtn">随机一个飞行点</div>
</body>
<script type="text/javascript">
var radar = $('.body').addRadar({
waveNum: 8,
frequency: 1,
radius: '400px',
centerPoint: { x: '450px', y: '450px'}
});
$('.addFlyBtn').on('click', function(){
radar.addFlyPoint();
if(!radar.scanInterval){
radar.scan();
}
});
</script>
</html>
本插件还有部分参数没有完成替代,有兴趣的同学可以自行补完。整个雷达效果其实代码很少,倒是为了加入飞行点扫描效果,增加了大量代码,有能力的同学,可以尝试帮忙简化一下这块的代码。


1515

被折叠的 条评论
为什么被折叠?



