知行札记
专题技术实践Web 界面

图表、地图、动效与本地化

把数据语义接到视觉编码和用户偏好,处理坐标、时间、语言、资源及反馈。

图形需要一份可解释的数据契约

图表先说明数据单位、范围、时间窗与聚合方式,再选择位置、长度、颜色或面积等编码。总量、比例与平均值回答不同问题;截断坐标、缺失值和不同分母可能改变读者判断。交互 tooltip 不能独占全部关键数据,必要摘要和表格应可取得。

Recharts 把数据和声明式图形组合在 React 中;ECharts 提供较完整的图表与交互配置;visx 适合自行组合底层形状。选择还需比较多轴、刷选、更新成本、服务端渲染和可访问要求,图库名称无法证明指标正确或用户理解。

尺寸变化可通过容器观察与重新布局处理。图实例若由命令式 API 管理,应有创建、更新和销毁边界。大量点可聚合或采样,规则及对极值的影响要公开,不能只因显示流畅就宣称没有信息损失。

命令式图表接线可把创建、更新和释放拆开。下例假设浏览器页面已经为容器设置尺寸,数据是无缺失、同单位的 { label, value };图表之外仍保留表格或文本数据。

import { useEffect, useRef } from 'react';
import * as echarts from 'echarts';

export function Bars({ rows }: { rows: { label: string; value: number }[] }) {
  const host = useRef<HTMLDivElement>(null);
  const chart = useRef<echarts.ECharts | null>(null);
  useEffect(() => {
    const node = host.current;
    if (!node) return;
    const instance = echarts.init(node);
    chart.current = instance;
    const observer = new ResizeObserver(() => instance.resize());
    observer.observe(node);
    return () => {
      observer.disconnect();
      instance.dispose();
      chart.current = null;
    };
  }, []);
  useEffect(() => {
    chart.current?.setOption({
      xAxis: { type: 'category', data: rows.map(row => row.label) },
      yAxis: { type: 'value' },
      series: [{ type: 'bar', data: rows.map(row => row.value) }],
    });
  }, [rows]);
  return <div ref={host} style={{ width: '100%', height: 320 }} />;
}

ResizeObserver 跟随容器变化,dispose 释放实例;数据变化只更新配置。按需模块导入可缩小产物,隐藏零尺寸容器重新显示时还要 resize。此片段没有执行真实布局或性能测试。ECharts 容器、尺寸与释放

地图怎样取得真实位置

底图、数据源、坐标参考与显示图层承担不同工作。经纬度顺序、坐标系、投影和数据许可应分别确认;某地区坐标转换和位置偏移不能靠替换底图猜测。

Leaflet 适合组合常见瓦片和覆盖物,MapLibre GL 面向 WebGL 的样式与矢量地图。地图 SDK 免费开源并不代表底图、地理编码和外部数据免费。GeoJSON 源更新、聚类、拾取和相机移动需要控制数据尺寸与生命周期。MapLibre GL JS

地图是交互界面,缩放按钮、键盘入口、标注文本和必要的位置列表帮助用户取得信息。地图不可用时仍应有可解释的失败状态;只保存坐标不代表有权公开位置。

MapLibre 可从一个无外部底图的样式建立本地 GeoJSON 图层。下例展示经纬度顺序、创建、load 后加源与层、最后移除实例;白色背景不提供道路或地理参照。

import { useEffect, useRef } from 'react';
import { Map } from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';

export function LocalPointMap() {
  const host = useRef<HTMLDivElement>(null);
  useEffect(() => {
    if (!host.current) return;
    const map = new Map({
      container: host.current,
      center: [0, 0], zoom: 2,
      style: {
        version: 8, sources: {},
        layers: [{ id: 'background', type: 'background',
          paint: { 'background-color': '#ffffff' } }],
      },
    });
    map.on('load', () => {
      map.addSource('points', { type: 'geojson', data: {
        type: 'FeatureCollection', features: [{
          type: 'Feature', properties: { label: '示例点' },
          geometry: { type: 'Point', coordinates: [0, 0] },
        }],
      } });
      map.addLayer({ id: 'points', type: 'circle', source: 'points',
        paint: { 'circle-radius': 6, 'circle-color': '#1457c5' } });
    });
    return () => map.remove();
  }, []);
  return <div ref={host} style={{ height: 320 }} />;
}

实际数据可经 getSource 取得 GeoJSONSource 后 setData;需要缩放控制或数据拾取时注册相应控件和事件,保留位置列表与失败反馈。接入外部 style URL 后才会产生瓦片、字体等资源请求,其 CORS、配额与许可随来源核对。此例未运行 GPU、地图或网络检查。MapLibre 图层示例

动效怎样帮助辨认变化

动效可以连接源与目标、提示进度和确认动作。CSS transition 适合简单状态变化,关键帧适合明确时间轨迹,Web Animations API 提供程序控制;Motion、GSAP、Lottie 和 Rive 分别有声明式运动、时间线、设计资产和交互状态等使用方向。

选择工具时看是否需要物理运动、布局过渡、时间线或设计资产。计时、取消、重复与卸载需有明确规则。View Transitions 的支持、路由接线和降级按真实浏览器与框架核对,不能因 API 存在便认定全设备可用。

prefers-reduced-motion 提供系统偏好,产品还可保存显式选择。减弱运动时保留必要状态与完成反馈;权限、焦点移动和可见结果不要依赖动画结束事件才能成立。动画结束回调需识别实际目标,子元素事件冒泡可能造成错误完成。

简单过渡可由 CSS 消费偏好,业务状态直接随动作改变。下例在减少运动时移除过渡,最终展开状态仍由 aria-expanded 和内容可见性共同表示:

.panel { transition: opacity 160ms ease, transform 160ms ease; }
.panel[data-open="false"] { opacity: 0; transform: translateY(-4px); }
@media (prefers-reduced-motion: reduce) {
  .panel { transition: none; transform: none; }
}

透明元素仍可能接受点击或焦点;真实收起还需要组件负责 hidden、焦点返回和过渡期间的可操作性。CSS 片段只处理运动偏好,不承担完整展开组件。

语言、格式与时间区

消息是完整语义单元。翻译使用插值、复数和语言特有语序,避免拼接多个片段形成一句话。i18next 等库连接语言资源、命名空间和回退;Next 集成还要处理服务器与浏览器的初始语言一致。

服务器共享的可变全局语言实例可能把一个请求语言带到另一个请求。可创建请求独立实例,并向客户端传必要资源和语言;跨边界传输对象需符合框架序列化契约,不能把服务器实例直接当作普通 JSON。

Intl 格式化数字和日期,输入值仍需准确表示。界面语言、内容语言、区域和时区是可分别选择的条件。切换语言后,URL、缓存键、资源载入和页面方向也要同步到声明的政策。i18next 原始文档

请求独立实例可这样建立,语言先经服务允许集合解析;资源文件由部署版本提供,初始化完成后再取得字符串:

import { createInstance } from 'i18next';

export async function createRequestMessages(requestedLanguage) {
  const language = ['zh-CN', 'en'].includes(requestedLanguage)
    ? requestedLanguage : 'zh-CN';
  const messages = createInstance();
  await messages.init({
    lng: language,
    fallbackLng: 'zh-CN',
    resources: {
      'zh-CN': { translation: { greeting: '你好,{{name}}' } },
      en: { translation: { greeting: 'Hello, {{name}}' } },
    },
  });
  return { language, greeting: messages.t('greeting', { name: 'Reader' }) };
}

这是 JavaScript API 接线;大型资源按命名空间加载,客户端初始化采用服务器选择的语言与同版本资源。插值后的字符串进入文本节点;需要 HTML 时另有净化边界。它没有证明真实翻译质量或 SSR 水合效果。i18next 实例与初始化 API

商业授权、价格、API 和平台支持需要在实际采用时查当时条款;本页没有运行地图、动画或辅助技术验证。

最后更新于

本页目录