zoukankan      html  css  js  c++  java
  • 【前端】 在前端利用数学函数知识+box-shadow解波浪图形

    今天正在刷数学函数相关题目,刷到了下面这篇文章,哇哦~有意思。 利用cos和sin实现复杂的曲线。传送门在下面。

    CSS 技巧一则 -- 在 CSS 中使用三角函数绘制曲线图形及展示动画

    正巧在复习一些数学知识,遂动手实践了一把使用 数学中的函数  使用css画连续曲线。

    函数: 第一步

    在数学中 函数 是指 ,一组定义域通过一组表达式, 映射到一组值域,也就是说 函数 f(x) = x^2 表示一个集合,每个输入x,固定通过x^2返回一个值y,由此定义可得:

    当集合 X = {-2, -1, 0, 1, 2}  输入到函数f,得到的值域集合 Y = { y | y >= 0 }。  

    我们也可以通过列表格,更直观的列举出函数的值:

    当x = 1 时 y等于 1

    当x = 2时 y等于 4

    x= -2 -1 0 1 2 ...
    y= 4 1 0 1 4 ...

    由这个表格,我们可以在坐标轴画出关于y = f(x) = x^2的函数的样子:

    开口控制

    如果我们在y = x^2 加个负号会怎么样呢,y = f(x) = -(x ^ 2)

    图像会倒过来变成像 n 这样的样子?

     就这样,我们可以通过这个函数,得到两种曲线,正的u 和 反的n那么问题来了,要画任意曲线,那么意味着,曲线要可大可小,可以在图中的任意一个位置,要怎么办呢?

    嗯 仔细想想,如果函数 f(x) = x^2 再让它除以-2呢

    f(x) = x^2 / -2

    x= -2 -1 0 1 2 ...
    y= -2 -1/2 0 -1 /2 -2 ...

    手动画一下图像大约长下面这样:

    y会因为除以2变得更小(想象一下两侧的y值会变小),当x = 2 ,  y就会等于2, 这样的结果是曲线变宽。

    那么我们也可以知道 如果 换成 f(x) = x^2 * 2, 当x=2,y等于4,曲线会变窄。

    如果除以的数变成了负数,开口就会向下

    由上面我们可以得到一个可以控制曲线开口大小的函数

    也可以换算到 f(x) = x^2 / t 当t 大于0,曲线开口向上,t小于0,曲线开口向下

    左右偏移控制

    现在我们可以控制开口大小,那么怎么样控制曲线左右移动呢?

    假设左右偏移量是P

    设函数 f(x) = (x - P)^2,P = 1 得到下面的表格:

    x= -2 - 1 -1 - 1 - 1 - 1 - 1 3 - 1
    y= 9 4 1 0 1 4

    还是用图像,大概长这样:

    可以看到,P的取值影响图像的左右偏移

    上下偏移控制

    控制上下偏移,实际上就是控制函数 f(x) = x ^2的值y的大小,只需要将   f(x) = x ^2 - H  就可以控制上下啦

    假设上下偏移量是H

    设函数 f(x) = x^2 + H,H = 1 得到下面的表格:

    x= -2  -1 



    ...
    y= 5 2 1 2 4 ...

    图就不画啦,可以直接看到x=0时,顶点已经不再0上了,向上偏移了1位

    值域区间和宽度的关系

    什么是区间

    集合的语言,我们定义各种区间为:
    说人话就是有两种区间 开区间 与 闭区间
    开区间不包含0,闭区间包含0
     
    区间和宽度的关系
     
    我们要一个完整的半圆|半弧,那么必须要定义一个起始x和结束x,否则曲线就是无限延伸的没有意义
     
    我们从函数f(x) = x^2的图像上任意取最小x a 和最大x b,b - a就是x的定义区间,也就是函数f(x) = x^2的定义域:

    好了,理解了上面的东西,万事俱备,接下来就是更复杂一点的问题了!

    接下来,工程问题,曲线

    目标,使用函数

    实现开头引用文章中,利用 cos和sin实现的曲线。

    分析

    通过上面对函数的分析我们可以得到一个式子:

    设 抛物线开口 = T

    设 左右偏移 = P

    设 上下偏移 = H

    设 定义域 = [a, b] (开区间a到开区间b)

    函数 f(x) = (x - P) ^ 2 / T - H, T > 0 开口向上

    函数g(x) = (x - P) ^ 2 / (T) - H, T < 0 开口向下

    现在我们要使弧线A的结束点是弧线B的起始点,并且调换方向,那么:

    如图的推理过程,首先反转A,将A向下移动H,再向左移动P,得到一个新的弧度,以此类推递归:

    然后用js实现一个简单的算法如下:

     // g(x) = f(x-(b-a)) - 2* f(a), T < 0
         function g (x, T, P, range) {
                const [a, b] = range
                return f(x - (b - a), T, P, range) - 2 * f(0, T, P, range)
            }
    
         // 当 T < 0 相当于上面图中的 p(x) = (x - (- (T / B * f(b - a))))  / T, T < 0 
            // 当 T > 0 直接计算 f(x) = (x - P) ^ 2 / T, T > 0
            function f (x, T, P, range, s) {
                const [a, b] = range
                if (T < 0 && !s) {
                    return Math.pow(x - (-(T / b * f(b - a, T, P, range, true))), 2) / T
                }
                if (T > 0 || s) {
                    return Math.pow(x - P, 2) / T
                }
                
            }
            // 选择初始函数
            function getY (x, T, P, range) {
                if (T > 0) {
                    return f(x, T, P, range)
                } else {
                    return g(x, T, P, range)
                }
            }
            //获取一堆x,y点组成的集合, size = 波浪数量,origin=原点,item = 配置P H T变量,points和ysize为递归存储数据
            function GetPoints(size, origin, item, points = [], ysize) {
                if (ysize === undefined) {
                    ysize = size
                }
                if (size <= 0) {
                    return points
                }
                const z = size % 2 === 0
                const M = 1 // 密度
                const width = item.b - item.a // 宽度
                let i = width;  
                while (i >= -width) {
                    const point = [
                        (origin[0] + i) + (ysize - size) * (width * 2), // x
                        origin[1] + getY(i, (z ? item.T : -item.T), item.P, [ // y
                            item.a,
                            item.b
                        ])
                    ]
                    points.push(point)
                    i -= M;
                }
                GetPoints(size-1, origin, {
                    a: item.a,
                    b: item.b,
                    T: item.T,
                    P: item.P
                }, points, ysize)
                return points;
            }

    效果

    通过一连串懵逼式的计算和换算,我们有了一个可以获取固定数量相连的曲线,通过T控制开口,P控制x偏移,定义域[a,b]控制宽度,我们来实现骚操作:

     拉到本地跑一跑:

    <!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>Document</title>
        <style>
            .circle {
                position: absolute;
                 1px;
                height: 1px;
                background: #333;
                border-radius: 50%;
                left: 0px;
                top: 0px;
                transition: all 200ms;
            }
        </style>
    </head>
    <body>
        <div class="circle" id="circle"></div>
        <script>
            function g (x, T, P, range) {
                const [a, b] = range
                return f(x - (b - a), T, P, range) - 2 * f(0, T, P, range)
            }
    
            function f (x, T, P, range, s) {
                const [a, b] = range
                if (T < 0 && !s) {
                    return Math.pow(x - (-(T / b * f(b - a, T, P, range, true))), 2) / T
                }
                if (T > 0 || s) {
                    return Math.pow(x - P, 2) / T
                }
                
            }
    
            function getY (x, T, P, range) {
                if (T > 0) {
                    return f(x, T, P, range)
                } else {
                    return g(x, T, P, range)
                }
            }
            function GetPoints(size, origin, item, points = [], ysize) {
                if (ysize === undefined) {
                    ysize = size
                }
                if (size <= 0) {
                    return points
                }
                const z = size % 2 === 0
                const M = 1 // 密度
                const width = item.b - item.a // 宽度
                let i = width;  
                while (i >= -width) {
                    const point = [
                        (origin[0] + i) + (ysize - size) * (width * 2), // x
                        origin[1] + getY(i, (z ? item.T : -item.T), item.P, [ // y
                            item.a,
                            item.b
                        ])
                    ]
                    points.push(point)
                    i -= M;
                }
                GetPoints(size-1, origin, {
                    a: item.a,
                    b: item.b,
                    T: item.T,
                    P: item.P
                }, points, ysize)
                return points;
            }
    
            /**
             * 生成box-shadow参数
             */
            function getBoxShadow (color = '#333') {
                let points = GetPoints(6, [500, 100], {
                        a : 0,
                        b : 100,
                        T : 200,
                        P : 0
                    })
                
                // const s = []
                const s = points.map((point) => `${point[0]}px ${point[1]}px 0 0 ${color}`)
                return s.join(',')
            }
    
            document.querySelector('#circle').style.cssText = `box-shadow: ${getBoxShadow()}; transform: rotate(90deg) translate(-500px, -500px)`
    
        </script>
    </body>
    </html>

    一毛一样,大功告成。

    展望

    利用数学函数,我们也可以画出使用sin / cos一毛一样的曲线,更多的,我们也可以用它来描绘一个物体的运动动作,例如波浪运动,抛物线运动。

    甚至可以用css画苦逼脸:

    加点动画玩玩

     

    延续

    数学与编程,有时候真的是相依相承的东西。从工程的角度来说,数学和程序算法有非常重要的关系,推荐大家阅读《数学与泛型编程》(高效编程的奥秘),受益匪浅,感觉整个程序职业生涯有了一次很棒的升华!

    完。

  • 相关阅读:
    MFC Windows 程序设计>WinMain 简单Windows程序 命令行编译
    AT3949 [AGC022D] Shopping 题解
    CF643D Bearish Fanpages 题解
    CF643C Levels and Regions 题解
    CF241E Flights 题解
    CF671C Ultimate Weirdness of an Array 题解
    CF1592F Alice and Recoloring 题解
    GYM 102452E 题解
    CF494C Helping People 题解
    P5556 圣剑护符
  • 原文地址:https://www.cnblogs.com/ztfjs/p/frontmath.html
Copyright © 2011-2022 走看看