您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > CSS

CSS绘制声呐图的详细步骤,三角函数和CSS技术高阶应用

时间:2023-03-30 14:02:23  来源:今日头条  作者:lyf风去云往

css绘制声呐超声波图的详细步骤

1、需求简述;

2、CSS绘制90度扇形;

3、添加分隔线绘制成一个超声波图样式的声呐图;

4、添加声呐图深水样式的背景;

5、根据声呐探测器返回数据计算鱼的位置;

6、根据声呐探测器返回数据绘制水深标尺;

7、完整代码;

8、知识点学习。

1.需求简述:

最近做一个项目有这么一个需求,根据声呐探测器返回水下鱼的位置数据,把鱼显示到扇形内,声呐数据包含总水深、n条鱼的水深二维数组(如:[[3452,8569,…],[...],[...],[...],[...]],单位毫米),随机显示到扇形相应的深度处即可。为什么是二维数组呢?因为声呐探测器有5个探头(如图1所示),返回的数据是包含了5个探头的回波数据,回波数据数组里的一个数字就代表一条鱼的深度位置,现要把1、2、3探头的数据展示到声呐图中间那格,4、5号探头数据分别显示到声呐图左右两格。

图1:声呐探头示意图

下面是最终效果图

最终效果图

看到这里的前端同学可以先暂停想想怎么实现哦,看完的同学们可以在评论区自己的方法哦,我也想借鉴一下,我觉得可能用canvas可能会更好。

这里用的是 Vue2+Less。

CSS绘制一个90度扇形

这是第一个难点,这里我尝试过svg的方式,但是没搞定,最后还是回到css来吧。

首先css是没办法直接画一个扇形的,因为我们的html元素都是四边形的,就算加了圆角,也只能显示个圆形。这里就要用的css高阶一点点的写法了:css clip-path属性。clip-path 用法将在文章底部总结知识点介绍。

html

<section ref="sona" class="maxbox fxbox" style="background: #ccc;">
    <div class="sx_cp">
        <div class="round1 path"></div>
    </div>
</section>

section 标签为一个正方形,用于为扇形可以绝对定位,div class="sx_cp" 为扇形容器,div class="round1 path" 为扇形主体。

css

.fxbox {
    display: flex;
    align-items: center;
    justify-content: center;
}
.sx_cp {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 6%;
}
.round1 {
    width: 140%;
    height: 140%;
    position: absolute;
    top: -70%; // -140/2
    left: -20%; // (100-140)/2
}
.path {
    clip-path: polygon(0% 0%, 100% 0%, 50% 50%, 100% 100%);
    border-radius: 50%;
    background-color: rgba(0, 225, 239, .1);
    box-shadow: inset 0 0 40px 1px rgba(1, 240, 255, 0.85);
    border: rgba(1, 240, 255, 1) solid 1px;
    transform: rotateZ(180deg);
}

这段代码得到一个扇形雏形效果:

扇形雏形效果

有了这个雏形,接下来就明了了,再加3个逐渐缩小的扇形就可以了。

html

<div class="sx_cp">
    <div class="round1 path"></div>
    <div class="round2 path"></div>
    <div class="round3 path"></div>
    <div class="round4 path"></div>
</div>

css

.round2 {
    width: 100%;
    height: 100%;
    position: absolute;
    top: -50%; // -100/2
		left: 0%; // (100-100)/2
		z-index: 2;
}
.round3 {
    width: 60%;
    height: 60%;
    position: absolute;
    top: -30%;
    left: 20%; // (100-60)/2
		z-index: 3;
}
.round4 {
    width: 20%;
    height: 20%;
    position: absolute;
    top: -10%;
    left: 40%; // (100-80)/2
		z-index: 3;
}

添加以上代码后,得到效果:

超声波效果

添加分隔线绘制成一个超声波图样式的声呐图

添加两条倾斜的虚线作为分隔线,把扇形分成三格,虚线的倾斜和定位很关键。

html

......

<div class="round4 path"></div>

<div class="line1"></div>

<div class="line2"></div>

css

.line1 {
    border-left: rgba(0, 225, 239, .6) dashed 1px;
    width: 0;
    height: 70%;
    position: absolute;
    transform: rotateZ(165deg);
    top: -1%;
    left: 59%;
}
.line2 {
    border-left: rgba(0, 225, 239, .7) dashed 1px;
    width: 0;
    height: 70%;
    position: absolute;
    transform: rotateZ(195deg);
    top: -1%;
    left: 41%;
}

添加后得到以下效果:

效果

添加声呐图深水样式的背景

从上面图可以看出,扇形最大的半径就是最外面那个圆的半径,那就直接给最外面的圆添加一个背景图,就是整个扇形超声波图的背景了。

开始裁剪圆形得到扇形时,使用transform: rotateZ(180deg)翻转了圆形,使扇形朝下,所以现在我们需要用一个水面朝下的图片作为背景图素材。

翻转后的背景图素材

css

.round1 {
    background-image: url(./bg.jpg);
    background-position: center center;
    background-size: 100% 100%;
    width: 140%;
    height: 140%;
    position: absolute;
    top: -70%; // -140/2
    left: -20%; // (100-140)/2
}

再给section标签添加个好看协调点的背景,得到以下效果:

添加背景后的效果

根据声呐探测器返回数据计算鱼的位置

在vue文件的data模拟探测器回波数据:

data() {
   // 回波数据
    this.snData = {
        depth: 4568,
        temperature: 18,
        echo_data: [[3299, 3299, 3200, 3399, 3299, 4400, 1230], [3333, 2222],
                    			[1234, 3210], [1234], [3210, 1234, 5000, 5000]]
    };
    return {
        XYArr: [], // {left, top, transform: rotateZ(0deg)}
        scale: [], // 参考线刻度
        hh: 0, // 水的总深度
        temperature: 0, // 水温
        count: 0 // 统计数
    };
},

 

下面是计算鱼的位置的关键代码,利用了三角函数的正弦函数sinθ、余弦函数cosθ,对应的js方法:Math.sin(ag * Math.PI / 180)、Math.cos(ag * Math.PI / 180),在vue文件的methods新增方法:

/*
三角函数,获取鱼在屏幕上的显示位置
h鱼的水深度,
grid=1,2,3 鱼属于扇形的分区索引,
第一格角度 = 15~45,第二格= 0~15,第二格区分左右部分,第三格= 15~45,
 */
getXY(h, hh, grid = 2) {
    let ag = 0; // 角度
    let lr = 'L'; // 第二格随机区分左右, L/R

    // 1.根据鱼的grid随机取扇形角度
    if (grid === 1) {
        ag = Math.floor(Math.random() * 15 + 25);
    }
    if (grid === 2) {
        ag = Math.round(Math.random() * 15);
        lr = ag % 2 === 0 ? 'R' : 'L';
    }
    if (grid === 3) {
        ag = Math.floor(Math.random() * 15 + 25);
        lr = 'R';
    }

    // 2.获取扇形相关数据
    let sona = this.$refs['sona'];
    let L = sona.offsetWidth / 2;
    let r = $('.round1').width() / 2;

    // 3.计算
    let p = hh / r;
    let LL = p * L; // 屏幕像素转换为毫米长度

    let xy = {};

    let x = LL - h * Math.sin(ag * Math.PI / 180);
    let psx = x / (LL * 2);
    if (lr === 'L') {
        xy.left = psx * 100 + '%';
    } else {
        xy.left = (LL * 2 - x) / (LL * 2) * 100 + '%';
    }

    let y = h * Math.cos(ag * Math.PI / 180);
    xy.top = y / (LL * 2) * 100 + '%';

    // 4.随机取鱼图标的旋转角度
    let st = -60; let end = 240;
    let deg = Math.floor(Math.random() * (st - end) + end);
    xy.transform = 'rotateZ(' + deg + 'deg)';

    this.XYArr.push(xy);
},

遍历二维数组生成鱼的位置

// 生成鱼的位置
createFish() {
    this.XYArr = []; // 清空鱼的数据
    this.getScale(this.hh, this.snData.depth);
    this.snData.echo_data.forEach((item, idx) => {
        // idx = 0,1,3 为扇形中间格;2为左边格,4为右边格
        let gird = 2;
        if (idx === 2) {
            gird = 1;
        } else if (idx === 4) {
            gird = 3;
        }
        item.forEach(fitem => {
            if (fitem > this.hh) fitem = this.hh;
            if (fitem < 600) fitem = 600;
            this.getXY(fitem, this.hh, gird);
        });
    });
},

 

鱼的图标样式:

css

.fsIcon {
    position: absolute;
    z-index: 5;
    left: 0;
    top: 0;
    width: 26px;
    height: 10px;
    background: url(./yu.png) no-repeat center center;
    background-size: 100% 100%;
    margin-top: -2px;
    margin-left: -13px;
    transform: rotateZ(40deg); // -60~240
    opacity: .9;
}

 

html,for生成鱼图标

    .......
    <div class="line1"></div>
    <div class="line2"></div>
     <i v-for="(item, idx) in XYArr" :key="idx" class="fsIcon"  :style="item"></i> 

得到以下鱼的分布效果:

鱼的分布效果

根据声呐探测器返回数据绘制水深标尺

最后根据探测器返回的总水深,生成参考标尺。

html

<div class="ruler">
  <span v-for="item in scale" :key="item">{{ item }}米</span>
</div>

css

.ruler {
       width: 0;
       height: 70%;
       position: absolute;
       transform: rotateZ(45deg);
       top: -10%;
       left: 25%;

   span {
       white-space: nowrap;
       position: absolute;
       right: -100%;
       z-index: 3;
       font-size: 12px;
       color: #eee;

       &:nth-child(1) {
            top: calc(100% - 20px); // 140
        }
       &:nth-child(2) {
            top: 70%;
        }
       &:nth-child(3) {
            top: 40%;
        }
       &:nth-child(4) {
            top: 10%;
        }
   }
}

JAVAScript

// 扇形参考线刻度
getScale(old_hh, new_hh) {
    if (Math.abs(new_hh - old_hh) < 200) return; // 两次深度小于0.x米不作处理
    this.hh = new_hh;
    this.scale = [];
    for (let i = 0; i <= 3; i++) {
        let item = ((new_hh - new_hh / 4 * i) / 1000).toFixed(1);
        this.scale.push(item);
    }
}

最终效果展示

最终效果展示

完整代码

上文的css和js代码是完整的,下面是完整的html:

<div ref="sona" class="maxbox fxbox">
  <div class="sx_cp">
    <div class="round1 path"></div>
    <div class="round2 path"></div>
    <div class="round3 path"></div>
    <div class="round4 path"></div>
    <div class="line1"></div>
    <div class="line2"></div>
    <div class="ruler">
      <span v-for="item in scale" :key="item">{{ item }}米</span>
    </div>
    <i v-for="(item, idx) in XYArr" :key="idx" class="fsIcon"  :style="item"></i>
  </div>
</div>

知识点总结

1、简单了解CSS中的路径裁剪 clip-path

clip-path 属性使用裁剪方式创建元素的可显示区域。区域内的部分显示,区域外的隐藏。可以指定一些特定形状。

语法:clip-source|basic-shape|margin-box|border-box|padding-box|content-box|fill-box|stroke-box|view-box|none|initial|inherit;

属性值 描述

clip-source 用 URL 表示剪切元素的路径

basic-shape 将元素裁剪为基本形状:圆形、椭圆形、多边形或插图

margin-box 使用外边距框作为引用框

border-box 使用边框作为引用框

padding-box 使用内边距框作为引用框

content-box 使用内容框作为引用框

fill-box 使用对象边界框作为引用框

stroke-box 使用笔触边界框(stroke bounding box)作为引用框

view-box 使用最近的 SVG 视口(viewport)作为引用框。

none 这是默认设置,不剪辑

initial 设置属性为默认值。

inherit 属性值从父元素继承。

其中basic-shape属性的值有几种:

polygon多边形、circle圆形、ellipse椭圆、inset插图

三角形

clip-path: polygon(50% 0%, 0% 100%, 100% 100%);

菱形

clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);

梯形

clip-path: polygon(20% 0%, 80% 0%, 100% 100%, 0% 100%);

平行四边形

clip-path: polygon(25% 0%, 100% 0%, 75% 100%, 0% 100%);

斜角

clip-path: polygon(20% 0%, 80% 0%, 100% 20%, 100% 80%, 80% 100%, 20% 100%, 0% 80%, 0% 20%);

左箭头

clip-path: polygon(40% 0%, 40% 20%, 100% 20%, 100% 80%, 40% 80%, 40% 100%, 0% 50%);

右箭头

clip-path: polygon(0% 20%, 60% 20%, 60% 0%, 100% 50%, 60% 100%, 60% 80%, 0% 80%);

五角星

clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);

 

2、简单了解js三角函数

Math.sin()
sin 方法返回一个 -1 到 1 之间的数值,表示给定角度(单位:弧度)的正弦值。

Math.sin(x)   //函数返回一个数值的正弦值。x为弧度
Math.sin(0);           // 0
Math.sin(Math.PI / 2); // 1

Math.cos()
cos 方法返回一个 -1 到 1 之间的数值,表示角度(单位:弧度)的余弦值。

Math.cos(0);           // 1
Math.cos(Math.PI);     // -1
Math.cos(2 * Math.PI); // 1

Math.tan()
表示一个角的正切值。

Math.atan()
函数返回一个数值的反正切(以弧度为单位)

Math.atan(0);  // 0

已知两直角边Y,X长度,求夹角角度:

180*Math.atan(Y/X)/(Math.PI)

 

*创作不易,转载请注明出处并附上本文链接



Tags:CSS   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
12 个超级实用的 CSS 技巧
user-selectuser-select 属性可以用来控制用户是否能够选择文本。<div> <p>You can&#39;t select this text.</p></div><p>You can select this text.</p>CSS:div { width...【详细内容】
2023-12-19  Search: CSS  点击:(129)  评论:(0)  加入收藏
Vue里使用Tailwind CSS,这不是耍流氓吗
前言随着前端的发展,对前端页面的要求越来越高,而css的功能也越来越强大,但对于写css样式来说却是非常头疼的的事。因为前端页面的动画要求以及页面布局的精细管控,需要写大量的...【详细内容】
2023-12-11  Search: CSS  点击:(162)  评论:(0)  加入收藏
原生CSS嵌套使用,你学明白了吗?
如果你是一个前端开发人员,那么你应该使用过CSS预处理器以及预处理器中的嵌套特性。它一直是一个受欢迎的功能,我一直都在使用CSS预处理器。今年所有的主流浏览器都支持原生CS...【详细内容】
2023-12-06  Search: CSS  点击:(181)  评论:(0)  加入收藏
CSS_Flex 那些鲜为人知的内幕
前言Flex想必大家都很熟悉,也是大家平时在进行页面布局的首选方案。(反正我是!)。不知道大家平时在遇到Flex布局属性问题时,是如何查阅并解决的。反正,我每次记不住哪些属性或...【详细内容】
2023-12-06  Search: CSS  点击:(139)  评论:(0)  加入收藏
CSS:这几个伪类,你用了吗
## :root 伪类:root 伪类是匹配文档的根元素,很多时候,根元素也就是 html 元素,用 root 伪类来匹配根元素,目的就是解决根元素不是 html 的场景,比如根元素是 svg 的时候。 root...【详细内容】
2023-11-30  Search: CSS  点击:(169)  评论:(0)  加入收藏
新 CSS Math方法:Rem() 和 Mod()
CSS 添加了许多新的数学函数来补充旧有的函数(如 calc() 和最近的 clamp() )。这些函数最终都表示一个数值,但其工作原理的细微差别并不总是一开始就很清楚。本文介绍每个函数...【详细内容】
2023-11-23  Search: CSS  点击:(254)  评论:(0)  加入收藏
CSS 新功能:让编码更高效
CSS 是一种不断发展的语言。每一次迭代,它都会变得越来越好。因此,了解最新的 CSS 功能非常重要,这样你才能在项目中使用它们,减少对第三方库的依赖。本文将介绍一些即将推出的...【详细内容】
2023-11-16  Search: CSS  点击:(163)  评论:(0)  加入收藏
使用 CSS Grid 的响应式网页设计:消除媒体查询过载
前言你是否厌倦了在实现响应式网站时需要管理多个媒体查询?说再见复杂的代码,拥抱更简单的解决方案吧:CSS Grid。在这篇文章中,我们将踏上一场激动人心的 CSS Grid 之旅,发现它如...【详细内容】
2023-11-10  Search: CSS  点击:(275)  评论:(0)  加入收藏
CSS这几个函数很实用,也很简单
calc()CSS 的 calc 函数非常实用,很多情况下,我们都会用到这个函数。calc 函数支持加减乘除四种运算,但是,它也有限制: 运算符前后带有单位或者百分比的数值,只能进行加减,不能进行...【详细内容】
2023-11-08  Search: CSS  点击:(219)  评论:(0)  加入收藏
这三个CSS生成器千万别错过啦!
前言 大家好我是小卢,「新拟态风格」和「磨砂玻璃」风格都是让人眼前一亮的一种设计风格,已经成为了最具代表性的设计趋势之一。 但是我们在真正写 CSS 的时候不容易调整这个...【详细内容】
2023-11-03  Search: CSS  点击:(159)  评论:(0)  加入收藏
▌简易百科推荐
12 个超级实用的 CSS 技巧
user-selectuser-select 属性可以用来控制用户是否能够选择文本。<div> <p>You can&#39;t select this text.</p></div><p>You can select this text.</p>CSS:div { width...【详细内容】
2023-12-19  前端充电宝  微信公众号  Tags:CSS   点击:(129)  评论:(0)  加入收藏
原生CSS嵌套使用,你学明白了吗?
如果你是一个前端开发人员,那么你应该使用过CSS预处理器以及预处理器中的嵌套特性。它一直是一个受欢迎的功能,我一直都在使用CSS预处理器。今年所有的主流浏览器都支持原生CS...【详细内容】
2023-12-06  南城大前端  微信公众号  Tags:CSS   点击:(181)  评论:(0)  加入收藏
CSS_Flex 那些鲜为人知的内幕
前言Flex想必大家都很熟悉,也是大家平时在进行页面布局的首选方案。(反正我是!)。不知道大家平时在遇到Flex布局属性问题时,是如何查阅并解决的。反正,我每次记不住哪些属性或...【详细内容】
2023-12-06  前端柒八九  微信公众号  Tags:CSS   点击:(139)  评论:(0)  加入收藏
CSS:这几个伪类,你用了吗
## :root 伪类:root 伪类是匹配文档的根元素,很多时候,根元素也就是 html 元素,用 root 伪类来匹配根元素,目的就是解决根元素不是 html 的场景,比如根元素是 svg 的时候。 root...【详细内容】
2023-11-30  读心悦  微信公众号  Tags:CSS   点击:(169)  评论:(0)  加入收藏
让你开发更舒适的 Tailwind 技巧
免费体验 Gpt4 plus 与 AI作图神器,我们出的钱 体验地址:体验使用 Tailwind CSS,我避免了在 React 项目中复制大量 CSS 文件的麻烦,使网页开发变得更加迅速高效。虽然 Tailwind...【详细内容】
2023-11-28  大迁世界  微信公众号  Tags:Tailwind   点击:(175)  评论:(0)  加入收藏
Display和Visibility的区别,你了解了吗?
采用CSS实现元素隐藏的方法有很多种,比如定位到屏幕之外、透明度变换等。而常见的两种方式是将元素设置为display:none或者visibility:hidden。元素样式设置为display:none当...【详细内容】
2023-11-27  读心悦  微信公众号  Tags:Display   点击:(171)  评论:(0)  加入收藏
新 CSS Math方法:Rem() 和 Mod()
CSS 添加了许多新的数学函数来补充旧有的函数(如 calc() 和最近的 clamp() )。这些函数最终都表示一个数值,但其工作原理的细微差别并不总是一开始就很清楚。本文介绍每个函数...【详细内容】
2023-11-23  大迁世界  微信公众号  Tags:CSS   点击:(254)  评论:(0)  加入收藏
CSS 新功能:让编码更高效
CSS 是一种不断发展的语言。每一次迭代,它都会变得越来越好。因此,了解最新的 CSS 功能非常重要,这样你才能在项目中使用它们,减少对第三方库的依赖。本文将介绍一些即将推出的...【详细内容】
2023-11-16  大迁世界  微信公众号  Tags:CSS   点击:(163)  评论:(0)  加入收藏
使用 CSS Grid 的响应式网页设计:消除媒体查询过载
前言你是否厌倦了在实现响应式网站时需要管理多个媒体查询?说再见复杂的代码,拥抱更简单的解决方案吧:CSS Grid。在这篇文章中,我们将踏上一场激动人心的 CSS Grid 之旅,发现它如...【详细内容】
2023-11-10  前端YUE  微信公众号  Tags:CSS   点击:(275)  评论:(0)  加入收藏
2024年了,别只使用React,需要学习一下Vue,不然没出路了
最近,我的朋友因为不熟悉 Vue.js 而未能通过面试。她平时工作中大部分时间都在使用React,所以也懒得去了解其他前端框架。世界上所有的前端框架我们都应该熟悉吗?不,这是极其不...【详细内容】
2023-11-08  web前端开发  微信公众号  Tags:Vue   点击:(294)  评论:(0)  加入收藏
站内最新
站内热门
站内头条