昆虫有趋光性吗

概念解释

1. 向光性

Phototropism向性的一种,特指「植物」。有正向负向。比如说「茎」是正向光性,而「根」是负向光性

2. 趋光性

Phototaxis是一种生物对光靠近或远离的趋性,主要指「植物」和「自养生物」。

飞蛾的趋光行为的实际解释

飞蛾之类具有复眼的夜间飞行的昆虫,常被误认为具有趋光性,这是一个认知偏误。虽然飞蛾在实际行为上表现为趋向光源飞行,事实上这是因为人造光源(如:火把,电灯)的光线与自然光线(阳光,月光,星光)不同,前者呈放射状,后者接近平行光。自然状态下通过保持平行光线的夹角修正自己的飞行路线,保持直线飞行。而人造光源呈现放射状,干扰了飞蛾的判断,与光源保持锐角飞行的飞蛾就表现成螺旋状飞向光源。

推荐阅读: 昆虫为什么不会因趋光性齐刷刷地奔向太阳?

白天只有太阳作为光源的情况

太阳光源

夜晚人造光源的情况

人造光源

紫外线灭蚊灯有用吗?

没有什么卵用

推荐阅读: 灭蚊灯放家里使用有效吗

总结

有些你认为理所应当的事情,未必是事实

关于grid布局的一些思考

0. 基础概念

0.1. 什么场景

当一个设计者在进行排版初期时,想要对布局进行整体划分

0.2. 什么问题

只能局部设计,无法从宏观角度设计并逐步深入细节。

0.3. 解决方案

通过对布局进行简单的「数据结构」描述,比如「一棵树」描述,生成可视化的布局结果

0.4. 概念阐述

在容器上进行布局轨道的划分,当内容填充进去时会按照轨道排列

具体解释: Basic concepts of grid layout

1. 数据结构

从具象的角度来出发,一般我们的划分一个区域的过程是这样的:

  1. 先画一个「矩形」
  2. 将这个「矩形」进行分割,从而形成新的矩形区域

做一次最简单的划分:

  1. 先画一个「矩形」1
  2. 将这个「矩形」分为2个,形成了1.11.2
  3. 再将1.2分为2个,形成了1.2.11.2.2

那么可以表达为以下结构

1
|- 1.1
|- 1.2
   |- 1.2.1
   |- 1.2.2

使用对象和数组进行表达

{
  name: '1',
  children: [
    {
      name: '1.1'
    },
    {
      name: '1.2',
      children: [
        {
          name: '1.2.1'
        },
        {
          name: '1.2.2'
        }
      ]
    }
  ]
}

2. 嵌套关系

采用类似html的方式来进行表达

<area name="1">
  <area name="1.1"></area>
  <area name="1.2">
    <area name="1.2.1"></area>
    <area name="1.2.2"></area>
  </area>
</area>

3. 结构演化

仅凭最基础的数据结构,是无法进行视觉表达的,因为不知道这块区域有多大,也不知道它在哪儿。在视觉上,我们认为的「区域」是有「面积」概念。在这里我们通过「宽度」和「高度」对「区域」进行描述

假设我们做这样一个布局

area

3.1 宽高结构

{
  name: '1',
  width: 1024,
  height: 1024,
  children: [
    {
      name: '1.1',
      width: 512,
      height: 1024
    },
    {
      name: '1.2',
      width: 512,
      height: 1024,
      children: [
        {
          name: '1.2.1',
          width: 512,
          height: 512
        },
        {
          name: '1.2.2',
          width: 512,
          height: 512
        }
      ]
    }
  ]
}

按照以上结构,虽然对「面积」有了描述,但是对于「位置」并没有阐释。那我们再引入一个包含xy两个维度的坐标系。

我们用的「坐标」来描述一个「矩形」,我们需要四个

前提是矩形的相邻边都是互相垂直的

在一个平面直角坐标系中,一个的描述为

用4个点来表达一个矩形

我们可以认为矩形是4条线段组成的图形

  • 第一条线段:

  • 第二条线段:

  • 第三条线段:

  • 第四条线段:

3.2 坐标结构

{
  name: '1',
  coords: [
    {
      x: 0,
      y: 0
    },
    {
      x: 1024,
      y: 0
    },
    {
      x: 0,
      y: -1024
    },
    {
      x: 1024,
      y: -1024
    }
  ],
  children: [
    {
      name: '1.1',
      coords: [
        {
          x: 0,
          y: 0
        },
        {
          x: 512,
          y: 0
        },
        {
          x: 0,
          y: -1024
        },
        {
          x: 512,
          y: -1024
        }
      ]
    },
    {
      name: '1.2',
      coords: [
        {
          x: 512,
          y: 0
        },
        {
          x: 1024,
          y: 0
        },
        {
          x: 512,
          y: -1024
        },
        {
          x: 1024,
          y: -1024
        }
      ]
      children: [
        {
          name: '1.2.1',
          coords: [
            {
              x: 512,
              y: 0
            },
            {
              x: 1024,
              y: 0
            },
            {
              x: 512,
              y: -512
            },
            {
              x: 1024,
              y: -512
            }
          ]
        },
        {
          name: '1.2.2',
          coords: [
            {
              x: 512,
              y: -512
            },
            {
              x: 1024,
              y: -512
            },
            {
              x: 512,
              y: -1024
            },
            {
              x: 1024,
              y: -1024
            }
          ]
        }
      ]
    }
  ]
}

全部用点来表达显得非常复杂,考虑在web前端的场景中,我们来试图简化一下

既然是矩形,其表达可以如下

在浏览器的区域里,我们可以认定原点是在左上角。

  • 原点右侧x+
  • 原点左侧x-
  • 原点上方y-
  • 原点下方y+

原点坐标是(0,0),并且位于左上方

那么我们可以只通过4个参数值来表达一个区域:

  • x 左上原点x坐标

  • y 左上原点y坐标

  • width 矩形宽度,实际是右下点的x坐标

  • height 矩形高度,实际是右下点的y坐标

3.3 宽高和坐标

{
  name: '1',
  x: 0,
  y: 0,
  width: 1024,
  height: 1024
  children: [
    {
      name: '1.1',
      x: 0,
      y: 0,
      width: 512,
      height: 1024
    },
    {
      name: '1.2',
      x: 512,
      y: 0,
      width: 512,
      height: 1024
      children: [
        {
          name: '1.2.1',
          x: 512,
          y: 0,
          width: 512,
          height: 512
        },
        {
          name: '1.2.2',
          x: 512,
          y: 512,
          width: 512,
          height: 512
        }
      ]
    }
  ]
}

到这一步,我们发现这其实就是「绝对定位」,那么其缺点也显而易见了。就是每一块区域都是独立的,相互之间的关系需要你严格设定。

大多数的设计师在布局的时候都是先画一个区域,然后再画一个区域,逐个进行调整。然后带来的问题就是,设计师最讨厌用设计工具做「表格」类的东西。

为什么会这样?

因为设计工具提供的布局方式就是「绝对定位」

根据以上所示,绝对定位里的概念总结起来就只有一个,叫做「坐标」

3.4 方向、继承以及分配

从上面的结构,我们可以发现几个事情

  • 1的原点和1.1的原点是同一个
  • 1.2的宽度等于1的宽度减去1.1的宽度
  • 1.2.11.2.2的宽度等于1.2的宽度

我们要完成的是「布局」而非「绘图」,所以我们可以让这件事情变得更加简单一些。我们分步来说明

3.4.1 划定基础区域

我们首先来制作一块区域,它的名字叫1,并且宽度是1024、高度也是1024,原点位置为(0,0)(0,0)我们把它设为缺省值,所以不表达。

{
  name: 1,
  width: 1024,
  height: 1024
}

3.4.2 引入分割方向概念

我们现在对其分割,那么这个时候就产生了一个问题,我们是按「水平」方向分割,还是按「垂直」方向分割?因此,我们引入方向(维度)概念。我们简单的定义一下

  • column ,即在「水平」方向上进行分割
  • row , 即在「垂直」方向上进行分割

那么我们对1进行水平方向上的分割

{
  name: '1',
  width: 1024,
  height: 1024,
  split: 'column'
}

3.4.3 引入份数概念

那么分割为几份呢?这时候我们又要引入一个份数的概念,那么我们现在就分成2份。

{
  name: '1',
  width: 1024,
  height: 1024,
  split: 'column',
  part: 2
}

3.4.4 在另一方向上的继承

这个时候我们意识到,因为是按「水平」方向分割,那么每一块的高度应该是和原来的区域是一样的,相当于每一块都继承了原有区域的高度,但是宽度因为被分割了,所以没法继承。

我们得考虑宽度的分配,现在我们将宽度进行了分割,那么每一块占原来宽度的多少呢?我们假设是均分,每一块就是50%,那么用一个值去表达

{
  name: '1',
  width: 1024,
  height: 1024,
  split: 'column',
  part: 2,
  distribute: 0.5
}

上面的结构可以表述为

有一块1024 * 1024 的区域,拆分成2列,每一列的宽度是1024 * 0.5

2column

同理如果是按「垂直」方向分割的话,则高度需要被分割,而宽度可以继承

3.4.5 引入等份单位

那如果不等分怎么办?我们的一个distribute不足矣表达,那么如果distribute是一个数组的话,数组的length其实就代表了part的数量,而且可以直接用对象的key来标识方向,所以可以形式上简化一下

{
  name: '1',
  width: 1024,
  height: 1024,
  columns: [0.4, 0.6]
}

但是以上结构会遇到所有的distribute之和不是1,既可能出现大于1,也可能出现小于1的情况。这时候我们引入一个「等份」的概念,叫做fr1fr代表空间的1等份

比如说,我们把空间分成3块,第一块占3fr,第二块占2fr

那么这两块区域的实际宽度是

  • 第一区域宽度:

  • 第二区域宽度:

这下原来的数据结构可以表达为

{
  name: '1',
  width: 1024,
  height: 1024,
  columns: [1fr, 1fr]
}

3.4.6 绝对数值

并不是所有的情况都是按等份来分配的。比如某块区域,我们想让它占据一个绝对数值的宽度。

{
  name: '1',
  width: 1024,
  height: 1024,
  columns: [200, 1fr, 2fr]
}

这个时候的计算其实是,把200宽度的区域从原来的里面扣除,在进行等份的计算

两块区域的实际宽度是

  • 第一区域宽度:

  • 第二区域宽度:

  • 第三区域宽度:

那如果全都是绝对数值会怎样

比如有一个区域宽是800,我们分3列,每一列宽度是200

{
  name: '1',
  width: 800,
  height: 800,
  columns: [200, 200, 200],
}

这个时候其实就是分开处理了,1本身是一个「区域」,3列构成的是另一个「区域」

如果分割的区域之和超出了被分割的区域又该怎么样呢?

其实和上面是一样的,实际上原有的「区域」和实际的「被分割区域」是分开处理的

总结一下就是

当「区域分割规则」都是「绝对数值」的时候,那就直接「划定区域」,而不去做「分割」

前端小伙伴这个时候都笑了,写了这么废话不就是CSS的grid么

我们把最初的数据结构来一次梳理

{
  name: '1',
  width: 1024,
  height: 1024,
  columns: [1fr, 1fr],
  children: [
    {
      name: '1.1'
    },
    {
      name: '1.2',
      rows: [1fr, 1fr]
      children: [
        {
          name: '1.2.1'
        },
        {
          name: '1.2.2'
        }
      ]
    }
  ]
}

是不是很简单,但是上面其他的所有部分是不是很复杂?

这就是为什么我们平时用别人做好的东西很简单,但是如果自己实现却无从下手的原因。

3.4.7 回归到坐标系

以上的结构我们再还原到坐标系画线的模式中,看看是怎样的

先画一个最大的「区域」1

lineTop: (0,0)(width,0) //(0,0)(1024,0)
lineRight: (width,0)(width,height) //(1024,0)(1024,1024)
lineBottom: (width,height)(0,height) //(1024,1024)(0,1024)
lineLeft: (0,height)(0,0) //(0,1024)(0,0)

1.1

lineTop: (0,0)(width*1/(1+1),0) //(0,0)(512,0)
lineRight: (width*1/(1+1),0)(width*1/(1+1),height) //(512,0)(512,1024)
lineBottom: (width*1/(1+1),height)(0,height) //(512,1024)(0,1024)
lineLeft: (0,height)(0,0) //(0,1024)(0,0)

1.2

lineTop: (width*1/(1+1),0)(width,0) //(512,0)(1024,0)
lineRight: (width,0)(width,height) //(1024,0)(1024,1024)
lineBottom: (width,height)(width*1/(1+1),height) //(1024,1024)(512,1024)
lineLeft: (width*1/(1+1),height)(width*1/(1+1),0) //(512,1024)(512,0)

1.2.1

lineTop: (width*1/(1+1),0)(width,0) //(512,0)(1024,0)
lineRight: (width,0)(width,height*1/(1+1)) //(1024,0)(1024,512)
lineBottom: (width,height*1/(1+1))(width*1/(1+1),height*1/(1+1)) //(1024,512)(512,512)
lineLeft: (width*1/(1+1),height*1/(1+1))(width*1/(1+1),0) //(512,512)(512,0)

1.2.2

lineTop: (width*1/(1+1),height*1/(1+1))(width,height*1/(1+1)) //(512,512)(1024,512)
lineRight: (width,height*1/(1+1))(width,height) //(1024,512)(1024,1024)
lineBottom: (width,height)(width*1/(1+1),height) //(1024,1024)(512,1024)
lineLeft: (width*1/(1+1),height)(width*1/(1+1),0) //(512,1024)(512,0)

4. 程序实现

我们发现「布局」的根本方式是「绝对坐标」,在之上抽象出了「栅格(grid)」。非常幸运的是,CSS已经提供了这层抽象。

如果做成vue组件的话,形式化方面不想采用近似iview<row> </col>嵌套结构,而是只有一个area组件,通过一个数据结构来完成area的递归

4.1 组件属性

基于以上的观察,我们可以构建一个组件叫做area,那么它应该包含以下属性

  • width 区域的宽度可选
  • height 区域的高度 可选
  • columns 区域按列切分
  • rows 区域按行切分
  • name 区域的名称

但是我们之前说过,在另一个方向上存在继承关系,那么应该避免同时设置columnsrows,另外考虑到宽度和高度可以从上级继承,我们做一次改进

  • width 区域的宽度可选
  • height 区域的高度 可选
  • grid: 可选columns rows
  • distribute: 分配规则
  • name 区域的名称

4.2 递归

按grid进行递归,如果发现没有grid,就停止递归

5. 用户体验

5.1 兼容性

不是所有的浏览器都支持grid,保险的做法是使用百分比

但是我们就不选择兼容方案了,激进一点

5.2 显性表达区域

用边框表达,还是用颜色表达

  • 用边框会占用区域
  • 颜色要求相邻的区域颜色不重复

5.3 模板

  • 左右
  • 上下
  • 圣杯

Vue组件抛事件和Function prop的差别

1. emit event

child component

<template>
  <button @click="foo">Button</button>
</template>
<script>
export default {
  name: 'my-button',
  methods:{
    foo () {
      this.$emit('onClick')
    }
  }
}
</script>

parent component

<template>
  <my-button @onClick="handleClick"></my-button>
</template>
<script>
import myButton from '@/components/myButton'
export default {
  components: { myButton }
  methods: {
    handleClick () {
      console.log('boo')
    }
  }
}
</script>

2. props function

child component

<template>
  <button @click="onClick">Button</button>
</template>
<script>
export default {
  name: 'my-button',
  props: {
    onClick: {
      type: Function
    }
  }
}
</script>

parent component

<template>
  <my-button :onClick="handleClick"></my-button>
</template>
<script>
import myButton from '@/components/myButton'
export default {
  components: { myButton }
  methods: {
    handleClick () {
      console.log('boo')
    }
  }
}
</script>

3. 实验结果和理解

父组件不知道事件什么时候会发生,因为事件的监听和处理都在子组件

子组件不知道父组件在事件发生后干什么,因为干什么是执行的父组件里的method

子组件要求父组件提供一个Callback函数,当事件发生时子组件去回调父组件提供的Callback函数

两种方式的运行结果是一样的,写法上和理解上有差别。

  1. 在emit的情况下

    父组件能够监听到事件的发生,并且决定在事件发生时做什么

  2. 在props的情况下

    父组件不知道事件发生与否,只能指令子组件在发生时做什么处理

4. 可能的问题

  1. 是否存在父组件要对子组件产生的事件进行搜集并处理? 第二种方式可能就歇菜
  2. 第二种方式对传入的Function内部是无法校验的,不过第一种也只是回避,本来就不校验