предисловие
После того, как компания выполнила множество проектов с большим экраном, существует множество решений для экранной адаптации, таких как медиа-запрос, масштабирование масштаба, Rem, vw и т. д., но каждое решение имеет свои специфические сценарии использования.Сталкиваясь с разными проектами, наши первое соображение заключается не в том, какая схема является лучшей, а в том, какая из них наиболее подходящая.
Подведя итоги проектов для больших экранов, выполненных нашей компанией, я написал это полное решение для адаптации на большие экраны, надеясь помочь вам.
Первый взгляд на визуализацию проекта
Проект 1: Система визуализации Metro 3D с большим экраном
Общее разрешение установлено на уровне 9600*2160, которое разделено на три экрана слева, посередине и справа, соединенных вместе, и не подходит для слишком большого или слишком маленького экрана.
Разрешение левого экрана: 2880*2160
Разрешение среднего экрана: 3840*2160
Разрешение правого экрана: 2880*2160
Левый, центр и правый эффект шитья
Проект 2: Платформа цифровых двойников Scenic Spot
Базовое разрешение — 1920*1080, и оно должно соответствовать экрану 16:9 вверх и вниз.
1920*1080
1280*720
2560*1440
3840*2160
7480*3240
Пункт 3: платформа ситуационной осведомленности xxx park
Множественное сращивание 2880*2160
2880*2160
Проект 4: комплексная платформа xxx для правоохранительных органов
6000*2160
Схема адаптации
Схема адаптации — это не только экранизация, но и входящие в нее иконки и DOM-элементы, которые представлены отдельно ниже.
экранизация
схема адаптации vw и vh
- Размеры согласно проекту,
px
Масштабируется доvw
а такжеvh
- Формула преобразования выглядит следующим образом
假设设计稿尺寸为1920*1080(做之前一定问清楚UI设计稿的尺寸)
即:
网页宽度=1920px
网页高度=1080px
我们都知道
网页宽度=100vw
网页宽度=100vh
所以,在1920x*1080px的屏幕分辨率下
1920px = 100vw
1080px = 100vh
这样一来,以一个宽300px和200px的div来说,其作所占的宽高,以vw和vh为单位,计算方式如下:
vwDiv = (300px / 1920px ) * 100vw
vhDiv = (200px / 1080px ) * 100vh
所以,就在1920*1080的屏幕分辨率下,计算出了单个div的宽高
当屏幕放大或者缩小时,div还是以vw和vh作为宽高的,就会自动适应不同分辨率的屏幕
Таким образом, хорошо конвертировать каждый раз, когда мы пишем css, но использовать калькулятор очень медленно, поэтому мы должны использоватьscss
функция, которая поможет нам рассчитать
установить scss
npm install sass@1.26.5 sass-loader@8.0.2 --save
Инкапсуляция функций инструмента расчета
- существует
src/styles
создать новыйutils.scss
файл, определите две переменные ширины и высоты проекта проекта - Используйте встроенный scss здесь
math.div
функция, определите дваvw
а такжеvh
расчетная функция - Мы передаем определенные значения пикселей, которые помогают нам автоматически рассчитать значение VW и VH
util.scss
//使用scss的math函数,https://sass-lang.com/documentation/breaking-changes/slash-div
@use "sass:math";
//默认设计稿的宽度
$designWidth:1920;
//默认设计稿的高度
$designHeight:1080;
//px转为vw的函数
@function vw($px) {
@return math.div($px , $designWidth) * 100vw;
}
//px转为vh的函数
@function vh($px) {
@return math.div($px , $designHeight) * 100vh;
}
конфигурация пути
Я использую проект vue, созданный vue2.6 и vue-cli3, поэтому мне нужно толькоvue.config.js
настроить егоutils.scss
path, его можно использовать глобально
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports={
publicPath: '',
configureWebpack: {
name: "app name",
resolve: {
alias: {
'@': resolve('src')
}
}
},
css:{
//全局配置utils.scss,详细配置参考vue-cli官网
loaderOptions:{
sass:{
prependData:`@import "@/styles/utils.scss";`
}
}
}
}
использовать в файле .vue
<template>
<div class="box">
</div>
</template>
<script>
export default{
name: "Box",
}
</script>
<style lang="scss" scoped="scoped">
/*
直接使用vw和vh函数,将像素值传进去,得到的就是具体的vw vh单位
*/
.box{
width: vw(300);
height: vh(100);
font-size: vh(16);
background-color: black;
margin-left: vw(10);
margin-top: vh(10);
border: vh(2) solid red;
}
</style>
Адаптация динамического элемента DOM
Иногда это может быть не только.vue
Используется в файлах, таких как элементы DOM, динамически создаваемые в js.
Он может быть отображен непосредственно в html
let oDiv = document.createElement('div')
document.body.appendChild(oDiv)
В этом случае я использую следующие два метода обработки, чтобы установить стиль созданного div.
1. Определите стиль глобального класса
существуетscr/styles
Создайте новый в каталогеglobal.scss
файл, представленный в main.js
global.css
.global-div{
width: vw(300);
height: vw(200);
background-color: green;
}
main.js
import './styles/global.scss'
При использовании установите className для созданного div
let oDiv = document.createElement('div')
oDiv.className = "global-div"
2. Определите функцию обработки в стиле js
Этот метод обработки аналогичен функции обработки scss, за исключением того, что для преобразования px в vw и vh используется чистый js.
существуетsrc/utils
Создайте новый в каталогеstyleUtil.js
файл, содержание следующее
//定义设计稿的宽高
const designWidth = 1920;
const designHeight = 1080;
let styleUtil = {
// px转vw
px2vw: function (_px) {
return _px * 100.0 / designWidth + 'vw';
},
// px转vh
px2vh: function (_px) {
return _px * 100.0 / designHeight + 'vh';
},
};
export default styleUtil;
При использовании установите свойства ширины и высоты отдельно
import styleUtil from "./src/utils/styleUtil.js"
let oDiv = document.createElement('div')
oDiv.style.width = styleUtil.px2vw(300)
oDiv.style.height = styleUtil.px2vh(200)
oDiv.style.margin = styleUtil.px2vh(20)
Однако у этого способа использования есть недостаток, то есть после изменения размера экрана его нужно обновлять вручную, чтобы завершить адаптивную настройку.
Адаптация и упаковка диаграммы Echarts
Зачем инкапсулировать?
- Конфигурация опций каждой диаграммы одинакова, и ее приходится каждый раз переписывать в бизнес-коде, что очень избыточно.
- В одном проекте все виды оформления диаграмм очень похожи, а то и одинаковы, нет необходимости все время повторять работу
- Могут быть некоторые разработчики, которые забывают учитывать характеристики обновления данных диаграмм и проблему адаптации при увеличении окна. Это приводит к тому, что данные обновляют вид диаграммы, но не обновляются, а масштабирование окна вызывает проблему деформации графики диаграммы.
Эффект, достигаемый после инкапсуляции
- Бизнес-данные и данные конфигурации стиля разделены, мне нужно передать только бизнес-данные
- Его размер полностью зависит от пользователя.
- Не будет проблем с деформацией из-за масштабирования, но он может хорошо адаптироваться
- Иногда стиль определенной диаграммы может немного отличаться, и я надеюсь сохранить гибкость настройки собственного стиля.
- Правильно обновлять представление независимо от того, какие данные передаются
- Если данные, которые я передаю, пусты, они могут показывать пустое состояние.
Предложение пакета
- Поместите все компоненты диаграммы в
components/Chart
в папке - Создайте отдельную папку для каждой диаграммы, например
components/Chart/PieChart
,components/Chart/LineChart
Ждать - Каждая диаграмма имеет значение по умолчанию
defaultOption.js
конфигурационный файл - График должен иметь
README.md
документ
зависимости
- "echarts": "^5.1.2",
- "element-resize-detector": "^1.2.3",
- "lodash": "^4.17.21",
Конкретная упаковка, в качестве примера возьмем круговую диаграмму
<template>
<h3 v-if="isSeriesEmpty">暂无数据</h3>
<div v-else class="chart">
</div>
</template>
<script>
import * as Echarts from "echarts"
import ResizeListener from "element-resize-detector";
import { merge, isEmpty } from "lodash";
import {basicOption} from "./defaultOption.js"
import {pieChartColor} from "./../color.js"
export default{
name: "PieChart",
props: {
// 正常的业务数据,对应echarts饼图配置中series[0].data
seriesData: {
type: Array,
required: true,
default: () => [],
},
// 表示需要特殊定制的配置
// 一般UI会规定一个统一的设计规范(比如颜色,字体,图例格式,位置等)
// 但不排除某个图标会和设计规范不同,需要特殊定制样式,所以开放这个配置,增强灵活性
extraOption: {
type: Object,
default: () => ({}),
},
},
data() {
return {
chart: null,
};
},
computed:{
isSeriesEmpty() {
return (
isEmpty(this.seriesData) || this.seriesData.every((item) => !item.value)
);
},
},
watch: {
seriesData: {
deep: true,
handler() {
this.updateChartView();
},
},
},
mounted() {
this.chart = Echarts.init(this.$el);
this.updateChartView();
window.addEventListener("resize", this.handleWindowResize);
this.addChartResizeListener();
},
beforeDestroy() {
window.removeEventListener("resize", this.handleWindowResize);
},
methods:{
/* 合并配置项和数据,对于需要自定义的配置项以及数据,使用merge函数将其合并为一个option */
assembleDataToOption() {
// 这部分的图例formatter取决于UI要求,如果你的项目中不需要,就可以不写formatter
// 由于echarts版本的迭代,这里的写法也有稍许改变
const formatter = (name) => {
const total = this.seriesData.reduce((acc, cur) => acc + cur.value, 0);
const data = this.seriesData.find((item) => item.name === name) || {};
const percent = data.value
? `${Math.round((data.value / total) * 100)}%`
: "0%";
return `${name} ${percent}`;
};
return merge(
{},
basicOption,
{ color: pieChartColor },
{
legend: { formatter },
series: [{ data: this.seriesData }],
},
this.extraOption
);
},
/**
* 对chart元素尺寸进行监听,当发生变化时同步更新echart视图
*/
addChartResizeListener() {
const instance = ResizeListener({
strategy: "scroll",
callOnAdd: true,
});
instance.listenTo(this.$el, () => {
if (!this.chart) return;
this.chart.resize();
});
},
/**
* 更新echart视图
*/
updateChartView() {
if (!this.chart) return;
const fullOption = this.assembleDataToOption();
this.chart.setOption(fullOption, true);
},
/**
* 当窗口缩放时,echart动态调整自身大小
*/
handleWindowResize() {
if (!this.chart) return;
this.chart.resize();
},
}
}
</script>
<style scoped="scoped" lang="scss">
.chart {
width: 100%;
height: 100%;
}
</style>
Адаптивная ширина и высота диаграммы
Здесь он напрямую установлен на 100%, и используются ширина и высота родительского элемента.Этот родительский элемент может напрямую использовать нашу инкапсулированную функцию scss.
.chart {
width: 100%;
height: 100%;
}
Шрифт диаграммы, интервалы и другие размеры являются адаптивными
- Размер шрифта echarts поддерживает только определенные значения (пиксели) и не может использовать размеры, такие как проценты или vw.Как правило, шрифты не будут адаптивными.При необходимости вы можете написать функцию адаптивной обработки для шрифта здесь.
- По умолчанию, вот пример вашего эскиза дизайна 1920*1080, то есть ширина веб-страницы 1920 пикселей (не забудьте спросить размер черновика дизайна пользовательского интерфейса, прежде чем делать это)
- Я написал эту функцию в отдельном файле инструмента
dataUtil.js
Внутри, звоните, когда нужно - Принцип заключается в том, чтобы рассчитать отношение текущей ширины экрана к ширине дизайна по умолчанию и умножить исходный размер на это значение.
- Кроме того, эту функцию можно использовать и для других элементов конфигурации электронных диаграмм, таких как интервалы, позиционирование и поля.
dataUtil.js
/* Echarts图表字体、间距自适应 */
export const fitChartSize = (size,defalteWidth = 1920) => {
let clientWidth = window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth;
if (!clientWidth) return size;
let scale = (clientWidth / defalteWidth);
return Number((size*scale).toFixed(3));
}
Я использую фреймворк vue и могу смонтировать эту функцию на прототипе.
import {fitChartSize} from '@src/utils/dataUtil.js'
Vue.prototype.fitChartFont = fitChartSize;
Таким образом, вы можете.vue
использовать прямо в файлеthis.fitChartSize()
передача
Конкретное использование инкапсулированной диаграммы
<template>
/** 饼图的父元素 **/
<div class="echart-pie-wrap">
<pie-chart :series-data="dataList" :extra-option="extraOption"></pie-chart>
</div>
</template>
<script>
import PieChart from "@/components/Chart/PieChart/PieChart.vue"
export default {
components: {
PieChart,
},
data() {
return {
dataList: [
{
name: "西瓜",
value: 20,
},
{
name: "橘子",
value: 13,
},
{
name: "杨桃",
value: 33,
},
],
extraOption: {
color: ["#fe883a", "#2d90d1", "#f75981", "#90e2a9"],
grid: {
top: this.fitChartSize(30),
right: this.fitChartSize(10),
left: this.fitChartSize(20),
bottom: this.fitChartSize(20) //间距自适应
},
textStyle: {
color: "green",
fontSize: this.fitChartSize(10) //字体自适应
},
},
};
},
};
</script>
<style lang="scss">
.echart-pie-wrap {
display: flex;
justify-content: center;
align-items: center;
width: vw(300);
height: vh(300);
margin: 1vw auto;
}
</style>
Описание исходного кода об инкапсуляции диаграммы
- В исходном коде я использовалlodashобщественная функцияmerge, который используется для объединения элементов конфигурации диаграммы. Последующие свойства исходного объекта перезаписывают предыдущие свойства с тем же именем.
- Еще одна функция для использованияisEmpty, когда бизнес-данные, которые я передаю, пусты, например пустой массив [], undefined, null, это будет рассматриваться как случай отсутствия данных. В это время мы покажем компонент пустого состояния, который может состоять из композиция фонового изображения;
- При привязке к определенному элементу DOM я не использовал селектор querySelector для выбора класса или идентификатора, сгенерированного Math.random, поскольку ни один из них не является абсолютно надежным, я напрямую использую корневой DOM, связанный с текущим элементом примера vue $ Эль
- Я слушаю изменения размера окна и добавляю соответствующий обработчик событий для этой ситуации — метод изменения размера, который идет с echarts, чтобы графика echarts не деформировалась.
- Установите ширину и высоту соответствующего DOM на 100%, чтобы его размер полностью контролировался контейнером, предоставленным пользователем.
- Второй параметр метода setOption указывает, не объединяется ли приходящая новая опция с предыдущей старой опцией, по умолчанию false, то есть слияние. Это, очевидно, невозможно, нам нужно, чтобы каждая бизнес-конфигурация была полностью независимой.
- Нейминг очень смысловой, его можно понять с первого взгляда
- Сохраняет гибкость необходимости настраивать некоторые пользовательские стили отдельно, а именно extraOption
- Отдельно отделите функцию адаптации шрифта диаграммы для обеспечения гибкости.
Что должен включать defaultOption.js?
- Обычно выставляет базовую конфигурацию
basicOption
просто хорошо - Если вам нужно иметь дело с адаптацией размера шрифта и интервала, пожалуйста, введите его здесь
fitChartSize
функция
import {fitChartSize} from "@src/utils/dataUtil.js"
export const basicOption = {
title: {
text: "某某手机数据",
subtext: "来自xx公司",
left: "center",
},
tooltip: {
trigger: "item",
},
legend: {
orient: "vertical",
left: "left",
textStyle: {
color: "RGB(254,66,7)",
fontSize: fitChartSize(10) //使用字体自适应
},
},
series: [
{
name: "手机购买占比",
type: "pie",
radius: "50%",
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
data: [], // 这里在使用的时候会被业务数据替换
},
],
}
Что делает element-resize-detector?
Это плагин для мониторинга изменения размера элементов DOM. Мы уже отслеживали масштаб окна, но иногда размер его родительского контейнера будет динамически изменяться. Мы отслеживаем ширину родительского контейнера.Когда размер родительского контейнера изменяется, echart может вызвать свой собственный метод изменения размера, чтобы сохранить нормальное представление.
Конечно, это не относится к случаю вкладки вкладки, На вкладке вкладки родительский контейнер меняется с display: none на фактическую clientWidth, что может быть выполнено до регистрации resizeDetector, поэтому подождите, пока не будет изменен размер родительского контейнера. отслеживается. , может быть слишком поздно.
решить эту проблему,Самый эффективный метод — вручную получить экземпляр echart через ref при переключении вкладок, и вручную вызвать метод resize, который является самым безопасным и эффективным..
Суммировать
Выше приведены все идеи адаптации проекта.Видно, что схема адаптации, принятая нашей компанией, относительно проста.В принципе, пиксели конвертируются в vw, vh.
Хотя это решение не самое совершенное, оно во многом отвечает потребностям нашего бизнеса.
Об исходном коде проекта
Поскольку проект не является открытым исходным кодом и не разрабатывается извне, пожалуйста, оставьте сообщение или личное сообщение, если вам это нужно.