js打印分页

打印

调用 window.print() 进行页面打印

分页

page-break-after CSS 属性调整当前元素之后的分页符。

  • auto:初始值。自动分页符(既不强制也不禁止)。
  • always: 始终在元素后强制分页。
  • avoid: 避免在元素后出现分页符。
  • left: 在元素之后足够的分页符,一直到一张空白的左页为止。
  • right: 在元素之后足够的分页符,一直到一张空白的右页为止。

page-break-before CSS 属性调整当前元素之后的分页符。

  • auto:初始值。自动分页符(既不强制也不禁止)。
  • avoid: 避免在元素前出现分页符。

page-break-inside CSS 属性调整当前元素之后的分页符。

  • auto:初始值。自动分页符(既不强制也不禁止)。
  • avoid: 避免在元素中出现分页符。

备注

若遇到在某些浏览器设置 page-break-after:always; 无法生效时,可以参考以下配置

1
2
3
4
5
6
7
8
9
10
11
12
<div style="display: block;position: relative;">
<div
style="page-break-after:always;page-break-inside: avoid;-webkit-region-break-inside: avoid;"
>
第一页
</div>
<div
style="page-break-after:always;page-break-inside: avoid;-webkit-region-break-inside: avoid;"
>
第二页
</div>
</div>

参考

高德地图

准备

  1. 注册账号并申请 Key ,参考 官方链接
  2. 通过 npm i @amap/amap-jsapi-loader 安装 SDK

使用

  1. 配置 securityJsCode
1
2
3
window._AMapSecurityConfig = {
securityJsCode: '你的 secret',
}
  1. 初始化 SDK
1
2
3
4
5
6
7
8
9
import AMapLoader from '@amap/amap-jsapi-loader'

AMapLoader.load({
key: '你的 key', // 申请好的Web端开发者Key,首次调用 load 时必填
version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: [], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
}).then((AMap) => {
// 初始化代码
})

注意

  • 通过此种方式使用高德地图时,建议 securityJsCode 在使用前做简单的加密,如通过 base64 等方式加密等
  • 当出现较大的误差时请检查 keysecurityJsCode 是否匹配

参考

pixi.js

Pixi 是一个非常快的 2D sprite 渲染引擎。

特点

  • 速度快:在 2D 渲染方面,PixiJS 是最快的。
  • 灵活:友好的、功能丰富的 API 让 PixiJS 轻松处理基本问题,同时您可以专注于生成令人难以置信跨平台体验。
  • 免费: PixiJS 永久开源,并拥有庞大的支持社区推动它的发展和演变。

安装

通过 npm 安装最新的 6.5.8 版本,7.x 版本暂时还有一些问题,建议使用 6.x 版本

1
npm i pixi.js@6.5.8

使用

  1. 创建应用,并把应用挂载到 dom 中
1
2
3
4
5
6
7
8
9
import PIXI, { Application, Sprite } from 'pixi.js'

const app = new Application({
width: 500,
height: 500,
antialias: true, // default: false 反锯齿
resolution: 1, // default: 1 分辨率
})
root.appendChild(app.view as any)
  1. 加载资源并把资源放在画布的中间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
app.loader.add('bunny', logo).load((loader, resources) => {
// This creates a texture from a 'bunny.png' image.
const bunny = new Sprite(resources.bunny.texture)

// Setup the position of the bunny
bunny.x = app.renderer.width / 2
bunny.y = app.renderer.height / 2

// Rotate around the center
bunny.anchor.x = 0.5
bunny.anchor.y = 0.5

// Add the bunny to the scene we are building.
app.stage.addChild(bunny)
})
  1. 让图片运动起来
1
2
3
4
5
6
7
8
9
10
// Listen for frame updates
app.ticker.add(() => {
// each frame we spin the bunny around a bit
// console.log('bunny.anchor.x', bunny.anchor)
if (bunny.x >= app.renderer.width) {
bunny.x = 0
} else {
bunny.x += 1
}
})

示例图片

示例图片

参考

Pixi.js 中文网

js获取方位信息

js 提供了获取方位信息的方法,整体可以分为以下几步

前置检查

通过判断 window 是否具有 DeviceOrientationEvent 这个属性来判断是否支持获取方位信息

1
2
3
function checkOrientation() {
return Boolean(window.DeviceOrientationEvent)
}

获取方位信息

通过监听事件实现对方位信息的获取

1
2
3
4
5
6
7
8
9
function watchOrientation(callback: (e: DeviceOrientationEvent) => void) {
if (checkOrientation()) {
window.addEventListener('deviceorientation', callback, true)
return () => {
window.removeEventListener('deviceorientation', callback, true)
}
}
return loop
}

参数

callback

获取方位信息后的回调,类型为 (e: DeviceOrientationEvent) => void

1
2
3
4
5
6
interface DeviceOrientationEvent extends Event {
readonly absolute: boolean // 一个布尔值,指示设备是否绝对提供方向数据。
readonly alpha: number | null // 表示设备绕z轴运动的数字,以度表示,值范围为0(包括)到360(不包括)。以正北方向为 0 度
readonly beta: number | null // 表示设备绕x轴运动的数字,以度表示,值范围为-180(包括)到180(不包括)。这表示设备的前后运动。
readonly gamma: number | null // 表示设备绕y轴运动的数字,以度表示,值范围为-90(包括)到90(不包括)。这表示设备从左到右的运动。
}

注意

  • 需要当前页面为 HTTPS 才被允许获取方位信息
  • 可以通过当前页面检测设备是否支持获取方位信息

参考

js获取位置信息

项目中需要获取定位信息,js 提供了对于的接口去获取,整体可以分为以下几步

前置检查

通过判断 navigator 是否具有 geolocation 这个属性来判断是否支持获取定位信息

1
2
3
function checkGeolocation() {
return 'geolocation' in navigator
}

权限校验

通过接口获取当前浏览器的定位授权状态

1
2
3
4
async function checkPermission() {
const res = await navigator?.permissions?.query({ name: 'geolocation' })
return res?.state || 'prompt'
}

授权状态共有 deniedgrantedprompt 三种状态

  • denied:拒绝授权
  • granted: 已经授权
  • prompt:需要用户手动授权

获取位置

一次获取位置

1
navigator.geolocation.getCurrentPosition(callback, handleError, options)

监听位置变化

1
2
3
4
5
6
7
8
9
// 注册事件监听
const geoWatchID = navigator.geolocation.watchPosition(
callback,
handleError,
options
)

// 清楚监听事件
navigator.geolocation.clearWatch(geoWatchID)

参数

callback

获取位置信息后的回调,类型为 (position: GeolocationPosition) => void

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface GeolocationCoordinates {
readonly accuracy: number // 表示纬度和经度属性精度的双精度值,以米为单位
readonly altitude: number | null // 海拔高度
readonly altitudeAccuracy: number | null // 一个双精度值,表示以米为单位的高度精度。此值可以为空。
readonly heading: number | null // 返回表示设备运行方向的双精度值。该值以度为单位,表示设备偏离正北方航向的距离。0度表示正北方,方向为顺时针方向(即东90度,西270度)。如果速度为0,航向为NaN。如果设备无法提供航向信息,则该值为空。
readonly latitude: number // 维度
readonly longitude: number // 经度
readonly speed: number | null // 返回表示设备速度的双精度值,单位为米/秒。此值可以为空。
}

/** Available only in secure contexts. */
interface GeolocationPosition {
readonly coords: GeolocationCoordinates // 表示设备在地球上的位置和海拔,以及计算这些属性的精确度
readonly timestamp: EpochTimeStamp // 表示获取到的位置的时间
}
handleError

获取位置信息失败的回调,类型为 (error: GeolocationPositionError) => void

1
2
3
4
5
6
7
8
9
10
interface GeolocationPositionError {
/**
* 错误原因
* 1 -> PERMISSION_DENIED: 地理位置信息的获取失败,因为该页面没有获取地理位置信息的权限。
* 2 -> POSITION_UNAVAILABLE: 地理位置获取失败,因为至少有一个内部位置源返回一个内部错误。
* 3 -> TIMEOUT: 获取地理位置超时,通过定义PositionOptions.timeout 来设置获取地理位置的超时时长。
**/
readonly code: number
readonly message: string // 描述错误的详细信息
}
options
1
2
3
4
5
interface PositionOptions {
enableHighAccuracy?: boolean // 表示应用程序希望接收最佳可能结果。如果为真,并且如果设备能够提供更准确的位置,它将这样做。请注意,这可能导致响应时间变慢或功耗增加(例如,使用移动设备上的GPS芯片)。另一方面,如果为假,则设备可以通过更快地响应和/或使用更少的功率来自由节省资源。默认值:false。
maximumAge?: number // 可接受返回的可能缓存位置的最大时间(以毫秒为单位)。如果设置为0,则表示设备无法使用缓存位置,必须尝试检索实际当前位置。如果设置为无穷大,则设备必须返回缓存位置,而不管其使用年限如何。默认值:0。
timeout?: number // 表示设备返回位置所允许的最大时间长度(以毫秒为单位)。默认值为无穷大,这意味着getCurrentPosition()在位置可用之前不会返回。
}

注意

  • iOS或部分 Android 设备需要当前页面为 HTTPS 才被允许获取位置信息
  • 可以通过当前页面检测设备是否支持获取位置信息

参考

git merge和rebase

相同点

  • 都是用来合并分支,将不同分支的代码融合在一起

不同点

生成的代码树不同

  • merge 生成的代码树记录了所有的历史操作过程
    merge 生成的代码树

  • rebase 生成的代码树是一条直线,通过对目标分支进行 “嫁接”,把新提交的 commit 代码添加到源分支的最后
    rebase 生成的代码树

处理冲突的方式

  • merge 命令合并分支,只需解决一次冲突,但是会产生一个新的 commit
  • rebase 命令合并分支,解决完冲突,执行 git add .和 git rebase –continue,不会产生额外的 commit,但是需要重复处理多次冲突。

附录

使用建议

当满足以下条件时:

  • 能够定期 rebase(避免 rebase 时出现大量冲突需要解决)
  • 开发的分支只是自己使用(避免其他人拉去代码时本地产生冲突)
  • 非基础公共分支

使用 rebase,其它使用 merge

基本使用

1
2
git rebase [source] [target]
# source 分支作为基线,把 target 分支的 commit 嫁接到 source 分支上,若 target 分支参数不存在则使用当前所在分支

小技巧

如果你不熟悉 git rebase,可以随时在临时分支中执行 rebase。

参考

js中浮点数精度问题

在商品的价格计算中,由于精度的需要,要把用户输入的价格从元转化为分,其转化规则如下

1
2
const price = Number(inputPrice) * 100
console.log(price)

但是在测试过程中发现,输入 2.01 时,输出结果不为 201 ,而是 200.99999999999997
通过研究发现和 js 的数字存储方式 IEEE 754 有关,计算机存储数字时,由于存储方式的原因,存储的并不是准确的数字,而是有一定误差的数字,如经典的

1
2
0.1 + 0.2 === 0.3
false

IEEE 754 浮点数

IEEE 754 浮点数表示

IEEE 754 浮点数由三个域组成,分别为 sign bit (符号位)、exponent bias (指数偏移值) 和 fraction (尾数)。64 位中,sign bit 占 1 位,exponent bias 占 11 位,fraction 占 52 位。

0.1 转换为二进制表示

小数转二进制用乘 R 取整的方法,运算如下

小数 x2 的结果 整数部分
0.1 0.2 0
0.2 0.4 0
0.4 0.8 0
0.8 1.6 1
0.6 1.2 1
0.2 0.4 0
0.4 0.8 0
0.8 1.6 1
0.6 1.2 1
0.2 0.4 0

结果 0.00011001100110011…(循环 0011), 将转换后的二进制通过科学计数法表示 0.00011...(无限重复 0011) 通过科学计数法表示则是 1.10011001...(重复 1001)*2 - 4(偏移 4 位)

指数偏移值

双精度浮点数固定偏移值 (2^(11-1)-1) 加上指数实际值(即 2^-4 中的 -4) 的 11 位二进制表示。

注: exponent bias 在 64 位中占 11 位
尾数

fraction 占 52 位所以抽取 52 位小数(多出来的采用四舍五入制)

结果

1
1001...(中间有 11 个 1001)...1010 (请注意最后四位,是 1010 而不是 1001,因为四舍五入有进位)

处理办法

可以通过四舍五入的方式进行取整,保证数据的准确性

toFixed

具备自动四舍五入的能力

1
2
const price = Number((Number(inputPrice) * 100).toFixed())
console.log(price)

参考

antd中form表单联动

日常开发过程中,经常涉及到 form 表单的联动,在 A 选项中的某个值选中时出现 BCE 选项,其它值选中时不显示

选中 A 时无选项

切换选项后新增选项

切换选项后新增选项

监听选项的 onChange 事件实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export function CustomForm() {
const [a, setA] = useState()
return (
<>
<Form.Item name="testa">
<Radio.Group onChange={(e) => setA(e.target.value)}>
<Radio value="A">A</Radio>
<Radio value="B">B</Radio>
</Radio.Group>
</Form.Item>
{a === 'B' && <Form.Item name="testb">表单项</Form.Item>}
</>
)
}

通过监听表单项的 onChange 事件来修改 statestate 值来驱动表单项的显示,官方已不建议在 form 表单中使用 onChange 事件

监听表单的 onValuesChange 事件实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export function CustomForm() {
const [a, setA] = useState()
return (
<Form
onValuesChange={(_, values) => {
const { testa } = values
setA(testa)
}}
>
<Form.Item name="testa">
<Radio.Group>
<Radio value="A">A</Radio>
<Radio value="B">B</Radio>
</Radio.Group>
</Form.Item>
{a === 'B' && <Form.Item name="testb">表单项</Form.Item>}
</Form>
)
}

通过监听表单的 onValuesChange 事件来修改 statestate 值来驱动表单项的显示

自定义组件 children 的渲染实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export function Advertise(props: RadioGroupProps) {
const { value, children, defaultValue, ...otherProps } = props
const isShowChildren =
(isUndefined(value) && defaultValue === 1) || value === 1
return (
<>
<Radio.Group {...otherProps} value={value} defaultValue={defaultValue}>
{AdActList.map((item) => (
<Radio key={item.value} value={item.value}>
{item.label}
</Radio>
))}
</Radio.Group>
{isShowChildren && children}
</>
)
}

通过自定义的表单驱动其它元素的渲染,把逻辑收束到组件内

提示:参考 antd 官方文档实现子定义表单控件

参考