Резюме и опыт адаптации мобильных терминалов и ПК-терминалов (2) (может быть более полным

внешний интерфейс

Часть 1☞Резюме и опыт адаптации мобильных терминалов и ПК-терминалов (1) (может быть более полным

Проблемы, вызванные веб-адаптацией

1. Browser mate указывает ядро

Обнаружение и идентификация метаэлементов браузера QQ. Введение в правила ядра:

Определить как ядро ​​​​хрома
  • тип документа стандартный
  • элемент метатега

пример:

<!DOCTYPE html>
<html>
  <head>
    <!-- 下面3个meta中任选一个,即可正确识别 -->
    <meta name="renderer" content="webkit" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="X-UA-Compatible" content="chrome=1" />
    <title>chrome core</title>
  </head>
  <body>
  meta webkit
  </body>
</html>
Определить как ядро ​​IE
  • тип документа нестандартный
  • обнаружение метаэлемента

пример:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <!-- 下面3个meta中任选一个,即可正确识别 -->
    <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
    <meta name="renderer" content="ie-comp" />
    <meta name="renderer" content="ie-stand" />
    <title>ie core</title>
  </head>
  <body>
  meta ie
  </body>
</html>

Ссылка: Документация браузера QQ

2. Проблема границы 1px

img

Причина появления:

Согласно размеру iPhone 6, рисунку шириной 750 пикселей, эти 750 пикселей на самом деле являются пикселем устройства iPhone6, 1 пиксель, измеренный при измерении чертежа дизайна, на самом деле равен 1 пикселю устройства, и когда мы устанавливаем область просмотра макета, равную идеальной области просмотра, равной до 375px, а поскольку DPR iPhone6 ​​равен 2, 1px при написании CSS соответствует 2 пикселям устройства, поэтому выглядит толще.

Решение

1.border-image

на основеmediaЗапрос для определения различных соотношений пикселей устройства с учетом разныхborder-image:

.border_1px{
          border-bottom: 1px solid #000;
        }
        @media only screen and (-webkit-min-device-pixel-ratio:2){
            .border_1px{
                border-bottom: none;
                border-width: 0 0 1px 0;
                border-image: url(../img/1pxline.png) 0 0 2 0 stretch;
            }
        }
2. background-image

иborder-imageТочно так же подготовьте квалифицированное фоновое изображение границы и смоделируйте его на фоне.

.border_1px{
          border-bottom: 1px solid #000;
        }
@media only screen and (-webkit-min-device-pixel-ratio:2{
.border_1px{
        background: url(../img/1pxline.png) repeat-x left bottom;
        background-size: 100% 1px;
            }
        }

Оба вышеперечисленных изображения нужно подготавливать отдельно, а с закругленными углами не очень легко обращаться, но они справятся с большинством сценариев.

3. Псевдокласс + преобразование

на основеmediaЗапрос для определения соотношения пикселей разных устройств для масштабирования линии:

.border_1px:before{
          content: '';
          position: absolute;
          top: 0;
          height: 1px;
          width: 100%;
          background-color: #000;
          transform-origin: 50% 0%;
        }
        @media only screen and (-webkit-min-device-pixel-ratio:2){
            .border_1px:before{
                transform: scaleY(0.5);
            }
        }
        @media only screen and (-webkit-min-device-pixel-ratio:3){
            .border_1px:before{
                transform: scaleY(0.33);
            }
        }

Этот метод может встречаться в различных сценариях, если вам нужно соблюсти закругленные углы, вам нужно только добавить псевдоклассы.border-radiusВот и все.

4. Используйте svg (помощь по плагинуpostcss-write-svg)

над намиborder-imageиbackground-imageможно смоделировать1pxГраницы, но все они используют растровые изображения и должны импортироваться извне.

с помощьюPostCSSизpostcss-write-svgмы можем напрямую использоватьborder-imageиbackground-imageСоздайтеsvgиз1pxРамка:

@svg border_1px { 
  height: 2px; 
  @rect { 
    fill: var(--color, black); 
    width: 100%; 
    height: 50%; 
    } 
  } 
.example { border: 1px solid transparent; border-image: svg(border_1px param(--color #00b1ff)) 2 2 stretch; }

После компиляции:

.example { border: 1px solid transparent; border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") 2 2 stretch; }

3. Адаптируйтесь к iPhoneX

Нам нужно разумно разместить верх и низ в безопасной зоне,iOS11добавил дваCSSфункцияenv、constant, который используется для установки расстояния между безопасной зоной и границей.

Внутри функции может быть четыре константы:

  • safe-area-inset-left: Расстояние от безопасной зоны до левой границы
  • safe-area-inset-right: Расстояние между безопасной зоной и правой границей
  • safe-area-inset-top: Расстояние от безопасной зоны до верхней границы
  • safe-area-inset-bottom: Расстояние от безопасной зоны до нижней границы

Примечание: мы должны указатьviweport-fitЗатем вы можете использовать эти две функции:

<meta name="viewport" content="width=device-width, viewport-fit=cover">

constantсуществуетiOS < 11.2действует в версии ,envсуществуетiOS >= 11.2, что означает, что нам часто приходится устанавливать их одновременно, чтобы ограничить страницу безопасной областью:

body {
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}

При использовании нижних фиксированных навигационных панелей нам нужно установить ихpaddingценность:

{
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}

Детали

Bump.IO/notes/2017/…

4. О горизонтальном экране

признание js

window.addEventListener("resize", ()=>{
    if (window.orientation === 180 || window.orientation === 0) { 
      // 正常方向或屏幕旋转180度
        console.log('竖屏');
    };
    if (window.orientation === 90 || window.orientation === -90 ){ 
       // 屏幕顺时钟旋转90度或屏幕逆时针旋转90度
        console.log('横屏');
    }  
});

CSS-распознавание

@media screen and (orientation: portrait) {
  /*竖屏...*/
} 
@media screen and (orientation: landscape) {
  /*横屏...*/
}

5. Проблема с размытием изображения

1 причина

Большинство изображений, которые мы обычно используем, являются растровыми изображениями (png、jpg..), растровое изображение состоит из пикселей, каждый пиксель имеет определенное положение и значение цвета:

img

Теоретически каждый пиксель растрового изображения отображается на экране с использованием физического пикселя для достижения наилучшего эффекта отображения.

пока вdpr > 1На экране растрового изображения пиксель растрового изображения может быть представлен несколькими физическими пикселями, однако этим физическим пикселям нельзя точно присвоить цвет соответствующего пикселя растрового изображения, и их можно только аппроксимировать, поэтому одно и то же изображение вdpr > 1будет размыто на экране:

img

2 решения

Чтобы обеспечить качество картинки, мы должны сделать так, чтобы один пиксель экрана максимально отображал один пиксель изображения, поэтому для разныхDPRэкран, нам нужно отображать картинки разного разрешения.

например: вdpr=2дважды отображает изображение на экране(@2x),существуетdpr=3на экране(@3x).

img

3 медиазапрос

использоватьmediaЗапросите и оцените различные соотношения пикселей устройства для отображения изображений с различной точностью:

.avatar{
            background-image: url(conardLi_1x.png);
        }
        @media only screen and (-webkit-min-device-pixel-ratio:2){
            .avatar{
                background-image: url(conardLi_2x.png);
            }
        }
        @media only screen and (-webkit-min-device-pixel-ratio:3){
            .avatar{
                background-image: url(conardLi_3x.png);
            }
        }

Только для фоновых изображений

4 image-set

использоватьimage-set:

.avatar {
    background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x );
}

Только для фоновых изображений

5 srcset

использоватьimgпомеченsrcsetсвойство, браузер автоматически подберет лучшее отображаемое изображение на основе плотности пикселей:

<img src="conardLi_1x.png"
     srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">
6 URL-адрес изображения сшивания JavaScript

использоватьwindow.devicePixelRatioПолучите соотношение пикселей устройства, просмотрите все изображения и замените адрес изображения:

const dpr = window.devicePixelRatio;
const images =  document.querySelectorAll('img');
images.forEach((img)=>{
  img.src.replace(".", `@${dpr}x.`);
})
7 Использование SVG

SVGПолное название масштабируемой векторной графики, которое отличается от растрового изображения на основе пикселей,SVGОн относится к описанию формы изображения, поэтому по сути является текстовым файлом небольшого размера и не будет искажаться, сколько бы раз его не увеличивали.

За исключением того, что мы вручную рисуем кодsvg, мы также можем использовать то же самое, что и растровое изображениеsvgрисунок:

<img src="conardLi.svg">
<img src="data:image/svg+xml;base64,[data]">
.avatar {
  background: url(conardLi.svg);
}

Ссылаться на:

Адаптация мобильного терминала

Адаптация мобильного терминала Toutiao

h5 связанные

что такое h5

Технология H5Коллекция мобильных веб-интерфейсов

img

Сама технология H5 используется длямобильная веб-страница. Поскольку само приложение имеет webviewКонтейнер, в котором может быть запущен код, связанный с веб-интерфейсом, и получена комбинация H5 и собственного приложения.Технология гибридных приложений.

Общий принцип технологии гибридных приложений

img

Мобильный адаптивный макет

Решение первое: rem + pxToRem

принцип
  1. Ширина окна экрана монитора назначаетсяhtmlизfont-size. В этом случае размер корневого шрифта будет меняться в зависимости от ширины экрана.
  2. будетpxпреобразовать вrem, Существуют две традиционные схемы, одна из которых заключается в использованииsass/lessпользовательская функция вpxToRem,Писатьpxкогда используешьpxToRemфункция преобразуется вrem. Другой - напрямую написатьpx, в процессе компиляции используются плагины для преобразования всехrem. такdomРазмер элемента будет меняться в зависимости от ширины экрана.
выполнить
  1. Динамически обновлять размер корневого шрифта
const MAX_FONT_SIZE = 420
// 定义最大的屏幕宽度
document.addEventListener('DOMContentLoaded', () => {
  const html = document.querySelector('html')
  let fontSize = window.innerWidth / 10
  fontSize = fontSize > MAX_FONT_SIZE ? MAX_FONT_SIZE : fontSize
  html.style.fontSize = fontSize + 'px'
})

  1. pxизменятьrem
pxToRemВариант первый
$rootFontSize: 375 / 10;
// 定义 px 转化为 rem 的函数
@function px2rem ($px) {
    @return $px / $rootFontSize + rem;
}
.demo {
    width: px2rem(100);
    height: px2rem(100);
}
pxToRemВариант 2

существуетvue-cli3Серединаpostcss-pxtoremПлагинов хватает, да и на других платформах примерно такая же идея.

const autoprefixer = require('autoprefixer')
const pxtorem = require('postcss-pxtorem')
module.exports = { 
  // ...
  css: {
    sourceMap: true,
    loaderOptions: {
      postcss: {
        plugins: [
          autoprefixer(),
          pxtorem({
            rootValue: 37.5,
            propList: ['*']
          })
        ]
      }
    }
  }
}

продолжай исследоватьpostcss-pxtoremИсходный код плагина, чтобы увидеть принцип его реализации.

function createPxReplace (rootValue, unitPrecision, minPixelValue) {
    return function (m, $1) {
        if (!$1) return m;
        var pixels = parseFloat($1);
        if (pixels < minPixelValue) return m;
        var fixedVal = toFixed((pixels / rootValue), unitPrecision);
        return (fixedVal === 0) ? '0' : fixedVal + 'rem';
    };
}

pxпревратиться вremВ основном эта функция, конечно, там много настраиваемых параметров, основной принцип аналогичен нашему плану. Удобство в том, что не нужно каждый раз писатьpxНужно добавить функцию, и код станет намного понятнее.

все элементыpxбыть преобразованным вremШерстяная ткань? Это не обязательно.borderсерединаpxне должен поворачиватьсяrem, с участием другого1pxпроблем, подробно рассмотренных в предыдущей статье, избежатьpxизменятьrem,будетborderсерединаpxкапитализироватьPX/Px/pX

Решение второе: vh + vw

принцип

vwЕдиницы измерения относительно ширины области просмотра зависят от ширины.

выполнить

Подобно rem, используйте его напрямуюpostcss-px-to-viewportПлагин настроен, и способ настройки такой же, как иpostcss-pxtoremПочти такой же.То же самое верно и для плагинов

другие решения

план дефект
1 процент Высота не может быть в процентах
2 Запросы СМИ +metaсерединаviewport Разные устройства имеют разную ширину, и коэффициент масштабирования не может быть полностью определен.
3 flex Все еще не могу решить проблему превышения ширины

Все вышеперечисленные решения имеют фатальные недостатки, и не рекомендуется использовать их для выполнения расчетов мобильной верстки.

flexиremЛучше использовать в комплексе

мобильный шаблон быстрой настройки vue

Ссылаться на

Библиотеки с открытым исходным кодом

1. библиотека компонентов Vant

В библиотеке компонентов vant по умолчанию используетсяpxУкажите единицу измерения, используйте при необходимостиrem, используйте плагин напрямую для идеальной адаптации.

заvwсхема, вант так же можно добавить через плагиныpxПревратиться вvw,заvwМогут быть ямки.

2.ant-design-мобильная библиотека компонентов

Библиотека компонентов ant-design-mobile все еще используется.pxединица измерения

@hd: 1px; // 基本单位

// 字体尺寸
// ---
@font-size-icontext: 10 * @hd;
@font-size-caption-sm: 12 * @hd;
@font-size-base: 14 * @hd;
@font-size-subhead: 15 * @hd;
@font-size-caption: 16 * @hd;
@font-size-heading: 17 * @hd;

// 圆角
// ---
@radius-xs: 2 * @hd;
@radius-sm: 3 * @hd;
@radius-md: 5 * @hd;
@radius-lg: 7 * @hd;
@radius-circle: 50%;

// 边框尺寸
// ---
@border-width-sm: 1PX;
@border-width-md: 1PX;
@border-width-lg: 2 * @hd;

Краткое изложение проблем, с которыми столкнулся h5

img

1. скольжение ios не гладкое

Представление

Скольжение по странице вверх и вниз вызовет зависание, палец оторвется от страницы, и страница немедленно перестанет двигаться. Общая характеристика заключается в том, что скольжение не является плавным и отсутствует инерция скольжения.

причина

Почему свайп не плавный в веб-просмотре iOS и как он определяется?

Оказывается, в iOS 5.0 и более поздних версиях для скольжения определены два значения.autoиtouch, значение по умолчаниюauto.

-webkit-overflow-scrolling: touch; /* 当手指从触摸屏上移开,会保持一段时间的滚动 */
-webkit-overflow-scrolling: auto; /* 当手指从触摸屏上移开,滚动会立即停止 */

решение

1. Добавьте метод касания прокрутки в контейнер прокрутки.

будет-webkit-overflow-scrollingустановлено значениеtouch

.wrapper {
    -webkit-overflow-scrolling: touch;
}

Установите полосу прокрутки, чтобы скрыть:.container ::-webkit-scrollbar {display: none;}

может привести к использованиюposition:fixed;Фиксированные позиционированные элементы, которые прокручиваются вместе со страницей

2. Установить переполнение

установить внешнийoverflowзаhidden, установите элемент содержимогоoverflowзаauto. Если внутренний элемент выходит за пределы тела, происходит прокрутка, а часть за пределами тела скрывается.

body {
    overflow-y: hidden;
}
.wrapper {
    overflow-y: auto;
}

Сочетание этих двух продуктов еще лучше!


2. Раскрывающийся список границ выдвижения iOS отображается белым пустым

Представление

Нажмите и удерживайте палец на экране, чтобы потянуть вниз, и в верхней части экрана появится белая область. Нажмите и удерживайте палец на экране и потяните вверх, внизу появится лишняя белая область.

причина

В iOS перетаскивание пальца вверх и вниз по экрану вызываетtouchmoveсобытие. Объектом, вызванным этим событием, является весьwebviewКонтейнер, контейнер естественно будет перетаскиваться, а остальное будет пустым.

решение
1. Отслеживайте события, чтобы запретить скольжение

Существует три события мобильного касания, которые определяются как

1. touchstart :手指放在一个DOM元素上。
2. touchmove :手指拖曳一个DOM元素。
3. touchend :手指从一个DOM元素上移开。

Очевидно, что нам нужно контролироватьtouchmoveсобытие

touchmoveСкорость событий определяется реализацией и зависит от аппаратных возможностей и других деталей реализации.

preventDefaultметод, который предотвращает все действия по умолчанию, такие как прокрутка, в одной и той же точке взаимодействия.

Из этого мы нашли решение, слушаяtouchmove, позволяйте скользить там, где вам нужно скользить, и запрещайте скользить там, где вам не нужно скользить.

Стоит отметить, что мы хотим отфильтровать элементы с контейнерами прокрутки.

Реализация выглядит следующим образом:

document.body.addEventListener('touchmove', function(e) {
    if(e._isScroller) return;
    // 阻止默认事件
    e.preventDefault();
}, {
    passive: false
});
2. Компромиссы прокрутки заполняют пробелы и украшают другими функциями

Во многих случаях мы не можем решить эту проблему и изменить свой образ мышления. Судя по сцене,Мы можем использовать раскрывающийся список как функциональное действие.

Например: Обновить страницу после потянув вниз


3. Неопределенное поведение при увеличении или уменьшении масштаба страницы

Представление

Дважды коснитесь или сведите пальцы на элементе страницы, и страница увеличится или уменьшится.

причина

Сам HTML будет генерировать поведение увеличения или уменьшения масштаба Например, в браузерах ПК вы можете свободно управлять увеличением или уменьшением масштаба страницы. Но на мобильных устройствах нам такое поведение не нужно. Поэтому нам нужно запретить это неопределенное поведение, чтобы улучшить взаимодействие с пользователем.

Принципы и решения

HTML metaВ стандарте метатега естьviewportАтрибут, используемый для управления масштабом страницы, обычно используемый на мобильных терминалах. Как показано на следующем рисунке, MDN

<meta name="viewport" content="width=device-width, initial-scale=1.0">

Таким образом, мы можем установитьmaximum-scale,minimum-scaleиuser-scalable=noчтобы избежать этой проблемы

<meta name=viewport
  content="width=device-width, initial-scale=1.0, minimum-scale=1.0 maximum-scale=1.0, user-scalable=no">

Предотвратить увеличение страницы (когда мета не работает)

  window.addEventListener(
    "touchmove",
    function (event) {
      if (event.scale !== 1) {
        event.preventDefault();
      }
    },
    { passive: false }
  );

4. Задержка события клика и проникновение

Представление

элемент прослушиванияclickсобытие, щелчок по элементу вызывает временную задержку прибл.300ms.

Щелкните слой маски, после того как слой маски исчезнет, ​​нижележащий элемент щелкнет для срабатывания.

причина
Почему есть задержка клика?

В сафари в iOS, чтобы реализовать операцию двойного нажатия для увеличения, после 300 мс клика, если нет второго клика, выполнитеclickЩелкните Действие. То есть определить, вызвано ли поведение пользователя двойным щелчком мыши. Однако в приложении независимо от того, требуется ли двойное касание для увеличения,clickКаждый клик имеет задержку 300 мс.

Почему происходит проникновение клика?

При наложении двухслойных элементов привязывать к верхнему элементуtouchсобытие, привязка нижнего элементаclickсобытие. так какclickпроизошло вtouchПосле этого нажмите на верхний элемент, элемент исчезнет, ​​а сработает нижний элементclickсобытие, приводящее к эффекту клика.

Принципы и решения
Решение 1. Замените щелчок на сенсорный запуск

Как упоминалось ранее, мобильные устройства поддерживают не только клики, но и несколько сенсорных событий. Итак, наша основная идея сейчас состоит в том, чтобы использоватьtouchсобытие вместоclickсобытие.

будетclickзаменитьtouchstartне только решенclickСобытия все отложены, а проблема с проникновением тоже решена. Потому что проблема проникновения вtouchиclickГенерируется при смешивании.

использовать в родном

el.addEventListener("touchstart", () => { console.log("ok"); }, false);

использовать в vue

<button @touchstart="handleTouchstart()">点击</button>

Решения с открытым исходным кодом также предоставляютclickмероприятие, которое обеспечиваетtouchstartсобытие. как в вантеbuttonкомпоненты

img

Итак, можно лиclickсобытия заменены наtouchstartШерстяная ткань? Почему фреймворки с открытым исходным кодом по-прежнему даютclickА события?

Давайте представим ситуацию, которая требует одновременного нажатия и смахивания. еслиclickзаменитьtouchstartЧто случится?

Последовательность триггера события:touchstart, touchmove, touchend, click.

Легко представить, что когда мне нужноtouchmoveПри скольжении срабатывает первымtouchstartСобытие click уже вызвало конфликт?

Итак, в случае прокрутки рекомендуется использоватьclickиметь дело с.

В следующихfastclickБиблиотека с открытым исходным кодом также выполнила следующую обработку.

Основная цель – использоватьtouchstartсинтезclickсобытие, убедитесь, что оно не находится под прокручиваемым родительским элементом.

Решение второе: используйте библиотеку fastclick

использоватьnpm/yarnиспользовать после установки

import FastClick from 'fastclick';
FastClick.attach(document.body, options);

Аналогичным образом используйтеfastclickПосле библиотеки,clickПроблемы с задержкой и проникновением исчезли

По моей практике, раз речь идет о открытой библиотеке, то надо понимать принцип ее реализации. В основном он инкапсулирует существующую собственную коллекцию событий в более совместимую коллекцию событий.

исходный код фасткликаКод ядра небольшой, менее 1000 строк. Интересно узнать!


5. Проблема в том, что программная клавиатура толкает страницу вверх, загибает ее, но не откидывает назад

Представление

На телефоне Android коснитесьinputКогда поле отображается, клавиатура всплывает и толкает страницу вверх, в результате чего стиль страницы становится загроможденным.

При удалении фокуса клавиатура убирается, область клавиатуры остается пустой и не откидывается назад.

причина

У нас будет фиксированное дно в макете приложения. В некоторых версиях Android вылезет всплывающее окно ввода, которое распаковываетabsoluteиfixedПозиционируемый элемент. Уменьшает видимую область и загромождает макет.

Принципы и решения
план 1:

Мониторинг события ввода вне фокуса, откат назад, когда он не в фокусе

/iphone|ipod|ipad/i.test(navigator.appVersion) &&
  document.addEventListener(
    "blur",
    (event) => {
      // 当页面没出现滚动条时才执行,因为有滚动条时,不会出现这问题
      // input textarea 标签才执行,因为 a 等标签也会触发 blur 事件
      if (
        document.documentElement.offsetHeight <= document.documentElement.clientHeight &&
        ["input", "textarea"].includes(event.target.localName)
      ) {
        document.body.scrollIntoView(); // 回顶部
      }
    },
    true
  );
Сценарий 2:

Решение для программной клавиатуры, толкающей страницу вверх, в основном заключается в отслеживании изменения высоты страницы и принудительном восстановлении ее высоты до всплывающего окна.

// Это должно быть изменено на стиль, атрибут высоты

// 记录原有的视口高度
const originalHeight = document.body.clientHeight || document.documentElement.clientHeight;
window.onresize = function(){
  var resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;
  if(resizeHeight < originalHeight ){
    // 恢复内容区域高度
    // const container = document.getElementById("container")
    // 例如 container.style.height = originalHeight;
  }
}

Проблема, связанная с тем, что клавиатура не может вернуться назад, возникает в iOS 12+ и wechat 6.7.4+ и является относительно распространенной ошибкой при разработке WeChat H5.

Принцип совместимости, 1. Определить тип версии 2. Изменить видимую область прокрутки

const isWechat = window.navigator.userAgent.match(/MicroMessenger\/([\d\.]+)/i);
if (!isWechat) return;
const wechatVersion = wechatInfo[1];
const version = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
 
 // 如果设备类型为iOS 12+ 和wechat 6.7.4+,恢复成原来的视口
if (+wechatVersion.replace(/\./g, '') >= 674 && +version[1] >= 12) {
  window.scrollTo(0, Math.max(document.body.clientHeight, document.documentElement.clientHeight));
}

window.scrollTo(x-coord, y-coord)window.scrollTo(0, clientHeight)Вернуться к исходному видовому экрану


6. Проблема с адаптацией безопасной зоны для iPhone серии X

Представление

По бокам или внизу челки на голове есть челка, закрывающая текст, или пустое место на черном или белом фоне.

причина

iPhone X и его серия выше, все используютЛю Хайпин дизайниполноэкранные жесты. Голова, низ и бока нуждаются в специальной обработке. Чтобы адаптироваться к особой ситуации iPhone X.

решение

Настройте безопасную область, заполните опасную область и не выполняйте операции и не отображайте содержимое в опасной области.

Опасная область относится к неровной области головы, области горизонтальной полосы внизу, а также левой и правой триггерным областям.

img

Конкретными операциями являются:viewport-fit ,metaМетка установлена ​​наcover, чтобы получить заливку всех областей. Определяем принадлежность устройства к iPhone X, добавляем в низ головыадаптационный слой

viewport-fitЕсть 3 значения:

  • auto: это значение не влияет на исходный порт просмотра макета, и вся веб-страница доступна для просмотра.
  • contain: область просмотра масштабируется, чтобы соответствовать самому большому прямоугольнику, отображаемому в строке.
  • cover: область просмотра масштабируется для заполнения экрана устройства. настоятельно рекомендуетсяsafe area insetпеременная, чтобы важный контент не отображался за пределами дисплея.

Установите для окна просмотра значениеcover

<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, viewport-fit=cover">

Добавьте адаптационный слой

использоватьsafe area insetПеременная

/* 适配 iPhone X 顶部填充*/
@supports (top: env(safe-area-inset-top)){
  body,
  .header{
      padding-top: constant(safe-area-inset-top, 40px);
      padding-top: env(safe-area-inset-top, 40px);
      padding-top: var(safe-area-inset-top, 40px);
  }
}
/* 判断iPhoneX 将 footer 的 padding-bottom 填充到最底部 */
@supports (bottom: env(safe-area-inset-bottom)){
    body,
    .footer{
        padding-bottom: constant(safe-area-inset-bottom, 20px);
        padding-bottom: env(safe-area-inset-bottom, 20px);
        padding-top: var(safe-area-inset-bottom, 20px);
    }
}

safe-area-inset-top, safe-area-inset-right, safe-area-inset-bottom, safe-area-inset-left safe-area-inset-*Четыре прямоугольника, которые определяют внутренний прямоугольник края окна просмотраtop, right, bottomиleftпеременные окружения, чтобы содержимое можно было безопасно размещать без риска быть обрезанным непрямоугольным дисплеем.

Для прямоугольного окна просмотра, такого как обычный монитор ноутбука, значение равно нулю.

Для непрямоугольных дисплеев (таких как круглые циферблаты,iPhoneXНа экране все видно в пределах прямоугольника, образованного четырьмя значениями, заданными пользовательским агентом.

вenv()Использование env(<custom-ident> , <declaration-value>? ), первый параметр — это настраиваемая область, а второй — альтернативное значение.

вvar()Использованиеvar(<custom-property-name> , <declaration-value>?), роль вenv()Дает альтернативное значение на случай, если оно не подействует.

constant()одеялоcss2017-2018 находится в стадии проекта, неизвестно, стандартизирован ли он. Неизвестно, существует ли эта функция в других версиях браузера iOS и добавлена ​​ли она как совместимый процесс.

Подробности смотрите в ссылках в конце статьи.


7. Страница генерируется как картинка и проблема с QR-кодом

Представление

По работе возникает необходимость генерировать картинки или QR-коды для страниц. Возможно, первое, о чем мы думаем, это то, что его проще передать бэкенду для генерации. Но таким образом нам нужно передать весь код страницы на серверную часть, что потребляет слишком много сетевой производительности.

решение
Сгенерировать QR-код

Генерация QR-кода с помощью QRCode

import QRCode from 'qrcode';
// 使用 async 生成图片
const options = {};
const url = window.location.href;
async url => {
  try {
    console.log(await QRCode.toDataURL(url, options))
  } catch (err) {
    console.error(err);
  }
}

будетawait QRCode.toDataURL(url, options)присвоить изображениюurlПросто

сгенерировать изображение

в основном используютhtmlToCanvasгенерироватьcanvasхолст

import html2canvas from 'html2canvas';
html2canvas(document.body).then(function(canvas) {
    document.body.appendChild(canvas);
});

Но это не только здесь, потому что этоcanvasпричина. Изображения, генерируемые мобильным терминалом, относительно размыты.

мы используем новыйcanvasМетод сгенерирован несколько раз, поместите его в двойной контейнер для достижения более четкого эффекта и загрузите картинку по гиперссылке.Загрузите файл для простой реализации и обновите его позже для более полной реализации.

const scaleSize = 2;
const newCanvas = document.createElement("canvas");
const target = document.querySelector('div');
const width = parseInt(window.getComputedStyle(target).width);
const height = parseInt(window.getComputedStyle(target).height);
newCanvas.width = width * scaleSize;
newCanvas.height = widthh * scaleSize;
newCanvas.style.width = width + "px";
newCanvas.style.height =width + "px";
const context = newCanvas.getContext("2d");
context.scale(scaleSize, scaleSize);
html2canvas(document.querySelector('.demo'), { canvas: newCanvas }).then(function(canvas) {
  // 简单的通过超链接设置下载功能
  document.querySelector(".btn").setAttribute('href', canvas.toDataURL());
}

Установите по мере необходимостиscaleSizeразмер


8. Проблема с общим доступом к учетной записи WeChat

Представление

При разработке официальной учетной записи WeChat H5, если вы нажмете кнопку «Поделиться» внутри страницы для вызова SDK, метод не сработает.

решение

Добавьте слой маски, сделайте руководство по обмену.

Потому что нажатие кнопки «Поделиться» внутри страницы не может быть вызвано напрямую, а для работы функции общего доступа необходимо щелкнуть больше в правом верхнем углу.


9. Решения, связанные с H5 Call SDK

Родная связь с H5

решение

использоватьDSBridgeПоддерживает как iOS, так и Android

Документацию см. Ссылки

Команда SDK предоставляет методы
  1. Способ регистрацииbridge.register
bridge.register('enterApp', function() {
  broadcast.emit('ENTER_APP')
})
  1. метод обратного вызоваbridge.call
export const getSDKVersion = () => bridge.call('BLT.getSDKVersion')
Мониторинг событий и запуск
const broadcast = {
  on: function(name, fn, pluralable) {
    this._on(name, fn, pluralable, false)
  },
  once: function(name, fn, pluralable) {
    this._on(name, fn, pluralable, true)
  },
  _on: function(name, fn, pluralable, once) {
    let eventData = broadcast.data
    let fnObj = { fn: fn, once: once }
    if (pluralable && Object.prototype.hasOwnProperty.call(eventData, 'name')) {
      eventData[name].push(fnObj)
    } else {
      eventData[name] = [fnObj]
    }
    return this
  },
  emit: function(name, data, thisArg) {
    let fn, fnList, i, len
    thisArg = thisArg || null
    fnList = broadcast.data[name] || []
    for (i = 0, len = fnList.length; i < len; i++) {
      fn = fnList[i].fn
      fn.apply(thisArg, [data, name])
      if (fnList[i].once) {
        fnList.splice(i, 1)
        i--
        len--
      }
    }
    return this
  },
  data: {}
}
export default broadcast

Обратите внимание на то, чтобы наступить на яму

Прежде чем вызывать метод, убедитесь, что SDK предоставляет этот метод. Если Android предоставляет этот метод, метод появится в iOS.Ошибка вызова и другие всплывающие окна. Как это решить?

Предоставьте решение, является ли Android, iOS. Судя по устройству

export const hasNativeMethod = (name) =>
  return bridge.hasNativeMethod('BYJ.' + name)
}

export const getSDKVersion = function() {
  if (hasNativeMethod('getSDKVersion')) {
    bridge.call('BYJ.getSDKVersion')
  }
}

Для одной и той же функции требуются iOS и Android с одинаковым именем метода, с которым проще работать.


10.H5 Стратегии решения, связанные с отладкой

Представление

Отладочный код обычно делается дляПросмотр данныхиПоиск ошибок. Существует два сценария: один — отладка во время разработки и тестирования, а другой — отладка в производственной среде.

Зачем отлаживать в производственной среде? Иногда ошибка не может быть воспроизведена в тестовой среде, а тестовая среда несовместима с производственной средой, в этом случае требуется экстренная отладка производственной среды.

При разработке на стороне ПК мы можем напрямую выйти из консоли и использовать инструменты, предоставляемые браузером, для работы с инструментами разработки или просмотра журналов. Но что мы делаем внутри приложения?

Принципы и решения
1. vconsoleконсольный плагин

Он также очень прост в использовании

import Vconsole from 'vconsole'
new Vconsole()

img

Заинтересованы в том, чтобы увидеть основные принципы его реализации, наше внимание должно быть сосредоточено наКак vsconsole распечатывает все наши журналы Виртуальная консоль Tencent с открытым исходным кодом

Вышеупомянутый метод предназначен только для разработки и тестирования.Это не разрешено в производственной среде, поэтому при его использовании необходимо оценивать среду.

import Vconsole from 'vconsole'
if (process.env.NODE_ENV !== 'production') {
    new Vconsole()
}
2. Агент + шпион-отладчик

Операция немного хлопотная, но распишу подробно, примерно разделив на 4 шага

  1. Установите плагин (глобальная установка)
sudo npm install spy-debugger -g
  1. Мобильный телефон и компьютер подключены к одному и тому же Wi-Fi, а мобильный телефон настроен как прокси.

Установите HTTP-прокси мобильного телефона, IP-адрес прокси-сервера будет равен IP-адресу ПК, а портspy-debuggerстартовый порт

порт по умолчанию для шпионского отладчика: 9888

Android: Настройки - WLAN - долгое нажатие для выбора сети - изменить сеть - дополнительно - настройки прокси - вручную

IOS: Настройки - Wi-Fi - Выберите сеть, нажмите восклицательный знак, HTTP-прокси вручную

  1. Мобильный телефон открывает страницу H5 в браузере или приложении.
  2. Откройте веб-сайт журнала рабочего стола для отладки и щелкните адрес прослушивания консоли npm. Просмотр захвата пакетов и структуры страницы H5

Этот метод может отлаживать страницы сгенерированной среды без изменения кода и может удовлетворить большинство потребностей в отладке.

Источник: Сяосуо Цзюньшао

12 Сводка проблем с мобильным 100вх

Описание проблемы:

На прокручивающемся мобильном терминале требуется полноэкранная прокрутка (с помощью swiper).Поскольку высота мобильного терминала является переменной, используется макет 100vh, который очень гармоничен в симуляторе.В результате было обнаружено, что в мобильный терминал Браузеры Chrome и WeChat/safari, так как панель браузера и некоторые заголовки панели навигации вызывают различное отображение

Рендеринг 1: высота плагина swiper установлена ​​​​на innerHeight, что приводит к сбою браузера WeChat при прокрутке.

Рендеринг 2: 100vh фактически превышает высоту экрана, что приводит к блокировке некоторого контента.

Причина появления:

лучше избегать100vhНо зависимыйjavascriptчтобы установить высоту для полного просмотра окна просмотра.

Основная проблема заключается в том, что мобильные браузеры (Chrome и Safari) имеют функцию «справки», в которой адресная строка иногда видна, а иногда скрыта, изменяя видимый размер области просмотра,

Эти браузеры не100vhвысота подстраивается под видимую часть экрана при изменении высоты области просмотра, вместо100vhУстановите высоту браузера, чтобы скрыть адресную строку. В результате, когда адресная строка видна, нижняя часть экрана будет обрезана, нарушая100vhпервоначальное намерение.

Рендер 3:

Когда мы используем браузер iOS Safari, панель инструментов и адресная строка будут скрыты при пролистывании страницы.

Когда страница скользит вверх, панель инструментов будет скрыта, а window.innerHeight в это время изменится, но vh в css не изменится, 100vh = window.innerHeight, когда панель инструментов скрыта. (В IphoneXR window.innerHeight = 719px, когда панель инструментов отображается, и window.innerHeight = 833px, когда панель инструментов скрыта)

Не имеет значения, если ваша страница изначально спроектирована как длинная страница с возможностью перелистывания, но если ваша страница будет одностраничным приложением только с одним размером области просмотра, возникнут проблемы. потому что даже если вы установитеwidth: 100%, страница все равно будет прокручиваться, а когда прокрутка вверх достигнет определенного порога, панель инструментов будет скрыта, а макет будет совершенно хаотичным. И высота панели инструментов также отличается в альбомной и портретной ориентации.

Кроме того, если мы установим абсолютные или фиксированные атрибуты для элементов, чтобы зафиксировать их внизу, Safari не будет делать то, что мы хотим. Как упоминалось выше, часть вашей страницы фактически заблокирована панелью инструментов внизу, поэтому элементы, которые мы установили внизу, также будут заблокированы панелью инструментов.

Некоторые решения:

1. Используйте calc для динамического расчета высоты объектов, заблокированных навигацией (не пробовал)
 min-height: calc(100vh - 0.9rem) //0.9rem是挡住的高度
2. Используйте js для динамической установки высоты

установить высоту наwindow.innerHeightправильно установит высоту видимой части окна. Если адресная строка видна, тоwindow.innerHeightэто высота всего экрана. Если адресная строка скрыта, тоwindow.innerHeightбудет высотой видимой части экрана,

3. Используйте в проекте vue

${app}/src/app.vue

  mounted() {
    // First we get the viewport height and we multiple it by 1% to get a value for a vh unit
    let vh = window.innerHeight * 0.01
    // Then we set the value in the --vh custom property to the root of the document
    document.documentElement.style.setProperty('--vh', `${vh}px`)
    // We listen to the resize event
    window.addEventListener('resize', () => {
      // We execute the same script as before
      let vh = window.innerHeight * 0.01
      document.documentElement.style.setProperty('--vh', `${vh}px`)
    })
  },

${app}/views/foo.vue

<style lang="scss" scoped>
.container {
  height: 100vh; /* 在之前设置为100vh,可以兼容某些不支持自定义属性的浏览器。*/
  height: calc(var(--vh, 1vh) * 100 - 46px);
</style>
4. Используйтеheight:100%

Установите высоту на 100% для каждого уровня под телом.

* {
  padding: 0;
  margin: 0;
  border: 0;
  outline: 0;
  box-sizing: border-box;
}
html {
  width: 100%;
  height: 100%;
  border: 5px solid red;
  overflow: hidden;
  //没试验 明天实验一下
  height:-webkit-fill-available;
  
}
5. В сафари динамически определяйте высоту, используйте css

Высота панели инструментов составляет 75 пикселей, а свойство вычисления calc несовместимо с более ранней версией Safari. Вы можете использовать метод 4.

Установите высоту на 100% для каждого уровня под телом.

calc(100vh - 75px)
6. Что касается родительского контейнера, у которого нет высоты, высота дочернего элемента: 100%, что приводит к сворачиванию содержимого.

Описание сцены:

  <head>
        <style>
            .app { width: 100%;height: 100%;display: flex;flex: 1;flex-direction: column; }
            header { width: 100%;height: 75px; }
            main { flex: 1; }
            iframe { width: 100%;height: 100%;}
        </style>
    </head>
    <body>
        <div class="app">
            <header></header>
            <main>
                <iframe v-if="true"></iframe>           
              <div v-else></div>
            </main>
        </div>

Родительский элемент с высотой в процентах должен иметь определенную высоту и свойство высоты. В противном случае элемент с высотой в процентах должен по умолчанию иметь высоту: авто (высота содержимого).

Chrome автоматически компенсирует это, но Safari сочтет это недостатком, родительский элемент iframe не имеет заданной высоты, что напрямую приводит к сворачиванию высоты iframe (с высотой содержимого). Поэтому, когда окно браузера растягивается, фактическая высота содержимого iframe изменяется, что приводит к проблеме.

решение:

  1. Укажите высоту для данного родителя
main { flex: 1;height: calc(100% - 75px); }
height: calc(100vh - calc(100vh - 100%))
  1. Укажите высоту непосредственно дочернему элементу iframe
iframe { width: 100%; height: calc(100vh - 75px); }

Здесь нет необходимости в 100% - 75px, потому что 100% зависит от указанной высоты родителя, а vh напрямую зависит от браузера, а не от высоты окна.

7. Используйте -webkit-fill-available

(сафари бесполезно)

CSS

body {
  min-height: 100vh;
  /* mobile viewport bug fix */
  min-height: -webkit-fill-available;
}
html {
  height: -webkit-fill-available;
}
body {
  display: flex; 
  flex-direction: column;
  margin: 0;
  min-height: 100vh;
}

main {
  flex: 1;
}
8. Идеальное решение
<template>
  <div class="module">
    <div class="module__item">20%</div>
    <div class="module__item">40%</div>
    <div class="module__item">60%</div>
    <div class="module__item">80%</div>
    <div class="module__item">100%</div>
  </div>
</template>

<script>
export default {
  mounted() {
    // First we get the viewport height and we multiple it by 1% to get a value for a vh unit
    const vh = window.innerHeight * 0.01
    // Then we set the value in the --vh custom property to the root of the document
    document.documentElement.style.setProperty('--vh', `${vh}px`)

    // We listen to the resize event
    window.addEventListener('resize', () => {
      // We execute the same script as before
      const vh = window.innerHeight * 0.01
      document.documentElement.style.setProperty('--vh', `${vh}px`)
    })
  }
}
</script>

<style>
body {
  background-color: #333;
}

.module {
  height: 100vh; /* Use vh as a fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
  margin: 0 auto;
  max-width: 30%;
}

.module__item {
  align-items: center;
  display: flex;
  height: 20%;
  justify-content: center;
}

.module__item:nth-child(odd) {
  background-color: #fff;
  color: #f73859;
}

.module__item:nth-child(even) {
  background-color: #f73859;
  color: #f1d08a;
}
</style>

13 Сводка проблем браузера сафари

1. Высота входного тега в сафари по умолчанию

Выполняя совместимость с браузером, я обнаружил, что высота входного тега в браузере Safari Apple всегда используется по умолчанию.Решение в настоящее время состоит в том, чтобы добавить атрибут line-height для его установки;

Если настройка высоты ввода в браузере Safari не работает, обязательно установите высоту строки, а затем удалите собственный стиль пользовательского интерфейса iOS: -webkit-appearance: none;

14 Проблемы с гибкой версткой (расширение)

Каждый модульный блок в контейнере называется flex-элементом.

Пространство шпинделя: основной размер

Пространство поперечной оси: поперечный размер

1. гибкий контейнер

div{ display: flex | inline-flex; }

После установки гибкого макета в это время свойства float, clear и vertical-align дочерних элементов будут недействительными.

Высота div, где находится flex:1, адаптируется к оставшейся высоте экрана.

2. 6 свойств, установленных для контейнера flex

1. flex-direction: определяет направление главной оси (т.е. направление расположения элементов)
.container {
    flex-direction: row(默认) | row-reverse | column | column-reverse;
}

2. flex-wrap: определяет, могут ли элементы в контейнере быть обернуты

.container {
    flex-wrap: nowrap(默认) | wrap(项目主轴总尺寸超出容器时换行,第一行在上方) | wrap-reverse;(反向)
}
  1. flex-flow: сокращение для flex-direction и flex-wrap.(Бесполезный)

4. justify-content: определяет выравнивание элемента по главной оси.

.container {
    justify-content: 
      flex-start(默认值 左对齐)
    | flex-end(右对齐)
    | center 
    | space-between(两端对齐,只有两行的时候,分别在首尾)
    | space-around(每个项目两侧的间隔相等,项目之间的间隔比项目与边缘的间隔大一倍)
  	| space-evenly 子元素会均匀分布在容器内,同时额外的空间将会被子元素的两侧所分享
}

Помнитеjustify-contentтолько вКогда в коробке осталось место для размещенияИграть роль.

5. align-items: определяет выравнивание элементов по поперечной оси.

.container {
    align-items:
      stretch(默认值):如果项目未设置高度或者设为 auto,将占满整个容器的高度。
      flex-start (顶点对齐)
      flex-end	(尾点对齐)
      center
      baseline (项目的第一行文字的基线对齐)
}

6. align-content: определяет выравнивание нескольких осей.Если элемент имеет только одну ось, это свойство не будет работать

.container {
    align-content: flex-start | flex-end | center | space-between | space-around | stretch(默认值);
}

Когда вы устанавливаете flex-wrap на nowrap, у контейнера есть только одна ось, и, поскольку элементы не переносятся, не будет нескольких осей.

Когда вы устанавливаете flex-wrap для переноса, в контейнере может быть несколько осей, поэтому вам нужно установить выравнивание между несколькими осями.

Выравнивание такое же, как и для justify-contentОба конца/середина/эквидистантное выравнивание и т. д.

3. Свойства элемента Flex

1. порядок: определяет порядок элементов в контейнере. Чем меньше значение, тем выше порядок. Значение по умолчанию — 0.

Порядок установлен так, чтобы он мог выйти на передний план.

2. flex-basis: определяет пространство главной оси, занимаемое элементом, до выделения лишнего пространства Браузер вычисляет, есть ли на главной оси избыточное пространство в соответствии с этим свойством.

Значение по умолчанию: авто, которое является исходным размером элемента.В настоящее время ширина и высота элемента зависят от значения ширины или высоты.

Когда главная ось горизонтальна, когда установлен flex-basis, значение настройки ширины элемента будет недействительным. flex-basis необходимо использовать в сочетании с flex-grow и flex-shrink, чтобы быть эффективным.

  • Когда значение flex-basis равно 0 %, считается, что элемент имеет нулевой размер, поэтому даже объявление размера 140 пикселей бесполезно.
  • Когда значение flex-basis установлено автоматически, тогда заданное значение в соответствии с размером (если оно равно 100 пикселей), 100 пикселей не будут включены в оставшееся пространство.

3. flex-grow: определите коэффициент увеличения элемента.

Значение по умолчанию равно 0, т.е. если осталось место, не увеличивать

Когда все элементы расположены со значением flex-basis, остается еще место, тогда в игру вступает flex-grow.

Если все элементы имеют свойство flex-grow, равное 1, они будут делить оставшееся пространство поровну. (если так)

Если один элемент имеет свойство flex-grow, равное 2, а все остальные элементы равны 1, первый займет в два раза больше оставшегося места, чем другие элементы.

Конечно, если вы обнаружите, что после расстановки всех элементов со значением flex-basis места не хватает, а flex-wrap:nowrap, flex-grow в это время работать не будет, то вам нужно следующее свойство.

4. flex-shrink: определяет коэффициент уменьшения элемента.

По умолчанию: 1, т.е. элемент будет сжиматься, если не хватит места, отрицательные значения на это свойство не влияют.

Если свойство flex-shrink одного элемента равно 0, а других элементов равно 1, первый не будет сжиматься при недостатке места.

5. flex: сокращение от flex-grow, flex-shrink и flex-basis.

.item{
    flex:  <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
    0 1 auto(默认)
} 

Они следующие:

1. Когда значение flex является неотрицательным числом, число является значением flex-grow, flex-shrink равно 1, а flex-basis равно 0%.

.item {flex: 1;} (等分剩余空间)
//等价于
.item {
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: 0%;
}

2. Когда значение flex равно 0, соответствующие три значения равны 0 1 0%

.item {flex: 0;}(存在剩余空间,也不放大)
//等价于
.item {
    flex-grow: 0;
    flex-shrink: 1;
    flex-basis: 0%;
}

Наконец, пожалуйста, господа多多关注~~~ Я всегда буду стараться обновляться!!Будь квалифицированным носильщиком (смеется)
В этой статье есть ссылки на множество отличных статей других больших парней~ Некоторые ссылки можно не указывать, если есть какие-то пропуски, обязательно восполняйте ❤

(*^▽^*)В следующей главе я планирую поделиться высокочастотными тестовыми сайтами и ответами, появившимися в недавних интервью~!