Реализуйте компонент отображения изображения точки кипения Nuggets с помощью Vue

внешний интерфейс Vue.js CSS точка кипения

мотивация

В недавнем фоновом проекте нужно добавить новую функцию.Короче говоря, это система ответов на комментарии с картинками.Почитав несколько подобных систем, я решил сымитировать дизайн Nuggets Boiling Point, который прост и элегантен, и весь модуль Интерфейс в основном такой же, как и у Boiling Point, только некоторые функции отсутствуют (функции ссылок и тем не сделаны)

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

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

Функциональный анализ компонентов

Первым шагом для получения этого задания является тщательная проверка конкретной производительности и логики компонента отображения изображений Nuggets Boiling Point (вы можете перейти в модуль Boiling Point, чтобы попробовать его). логические ситуации. Ниже перечислены некоторые проявления компонентов
(1) Если есть только одно изображение, оно будет отображаться как большое изображение, как показано ниже.

(2) Если имеется несколько изображений (до 9 изображений), они будут отображаться в виде эскизов, и изображения будут расположены по-разному, если количество изображений разное.

Вышеприведенное изображение представляет собой расположение 8 изображений.Когда имеется 9 изображений, расположение имеет форму сетки из девяти квадратов.Следующее изображение представляет собой расположение 5 изображений.

Разница в том, что количество изображений в каждой строке зависит от общего количества изображений.

(3) Когда соотношение сторон изображения превышает определенный порог, длинная метка изображения отображается в правом нижнем углу изображения.

(4) Нажмите на любую миниатюру для переключения на подробное интерфейс дисплея изображения

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

(54) Нажмите кнопку «Просмотр большого изображения» на изображении выше, чтобы войти в компонент полноэкранного просмотра больших изображений.

Этот компонент может переключать изображения влево и вправо, и если отображается длинное изображение, сначала полностью отобразить длинное изображение в окне просмотра, как показано ниже.

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

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

Структурная абстракция компонентов

Сначала назовите весь компонент (компонент отображения новостного изображения), требуется только одна опора, то есть массив изображений, который содержит URL-адрес каждого изображения.
<message-image-viewer :imageList="imageList"></message-image-viewer>

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

<!--全屏大图组件-->
<full-screen-viewer
  :imageList="imageList"
  :currentImageIndex="currentImageIndex"
  @close="handleFullScreenViewerClose"
  v-if="isShowFullScreenViewer">
</full-screen-viewer>

Требуются два реквизита: первый — это массив отображаемых изображений, а второй — индекс отображаемого в данный момент изображения, который определяется его родительским компонентом.<message-image-viewer>входящий

Ниже<message-image-viewer>общая структура

<template>
  <div class="wrapper" ref="wrapper">
  
    <!--缩略图的div-->
    
    <div class="brief-view-wrapper" v-show="!isShowDetail" ref="outerWrapper">
      <!--如果是单张图-->
      <template v-if="isSingleImage">
        <div class="single-img-container"
             @click="showDetailImage(imageList[0])"
             :style="{backgroundImage:'url('+imageList[0]+')'}">
          <!--用div撑开图片-->
          <div class="ratio-holder" :style="{paddingTop: calcSingleImgHeight}">
          </div>
          <span class="long-image" v-if="isLongImage">长图</span>
        </div>
      </template>
      <!--多张图-->
      <template v-else>
        <!--图片放在backgroundImage属性中-->
        <div class="multiple-img-wrapper" :class="[colsOfMultipleImages]">
          <div class="multiple-img-container"
               v-for="(item,index) in imageList"
               @click="showDetailImage(item)"
               :style="{backgroundImage:'url('+item+')'}">
            <!--控制图片的高度和宽度一样,padding-top基于父元素的宽度-->
            <div class="ratio-holder" style="padding-top: 100%">
            </div>
            <span class="long-image" v-show="isLongImageList[index]">长图</span>
          </div>
        </div>
      </template>
    </div>
    
    <!--详情图的div-->
    
    <div class="detail-view-wrapper" v-show="isShowDetail" ref="detailViewWrapper">
      <!--顶部操作栏-->
      <div class="top-panel">
        <div class="panel-item" @click="hideDetailImage">
          <i class="iconfont icon-zoomout icon-pos"></i>
          <span>收起</span>
        </div>
        <div class="panel-item" @click="showFullScreenViewer">
          <i class="iconfont icon-zoomin icon-pos"></i>
          <span>查看大图</span>
        </div>
        <div class="panel-item" @click="handleImageRotate(-1)">
          <!--inline-block才能旋转,inline不行-->
          <i class="iconfont icon-reload icon-pos"
             style="transform: rotateY(-180deg);display:inline-block;">
          </i>
          <span>向左旋转</span>
        </div>
        <div class="panel-item" @click="handleImageRotate(1)">
          <i class="iconfont icon-reload icon-pos"></i>
          <span>向右旋转</span>
        </div>
      </div>
      <!--中间图片展示栏,注意需要设置高度,因为里面的img是绝对定位-->
      <div class="detail-img-wrapper" :style="{height:outerDivHeight+'px'}">
        <!--加载的logo-->
        <div class="detail-img-loading">
          <circle-loading fillColor="#9C9C9C" v-if="!isDetailImageLoaded">
          </circle-loading>
        </div>
        <img src=""
             ref="detailImage"
             v-show="isDetailImageLoaded"
             :style="detailImageStyle"
             class="detail-img">
        <!--点击隐藏详情图的div-->
        <div class="toggle-zoomout" @click="hideDetailImage">
        </div>
        <!--上一张图片的div-->
        <div class="prev-img" @click="switchImage(-1)" v-if="currentImageIndex > 0">
        </div>
        <div class="next-img" @click="switchImage(1)" v-if="currentImageIndex<imageList.length-1">
        </div>
        <!--全屏大图组件-->
        <full-screen-viewer
          :imageList="imageList"
          :currentImageIndex="currentImageIndex"
          @close="handleFullScreenViewerClose"
          v-if="isShowFullScreenViewer">
        </full-screen-viewer>
      </div>
      
      <!--缩略图展示栏-->
      
      <div class="small-img-wrapper">
        <div class="small-img-container"
             @click="switchSmallImage(index)"
             :class="{'small-img-active':currentImageIndex === index}"
             :style="{backgroundImage:'url('+item+')'}"
             v-for="(item,index) in imageList">
        </div>
      </div>
    </div>
  </div>
</template>

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

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

Анализ раздела миниатюр

Давайте сначала проанализируем часть миниатюр. Миниатюра - это то, как выглядят миниатюры изображений, упомянутых выше. Из предыдущего анализа мы знаем, что одно изображение отображается как большое изображение, а когда имеется несколько изображений, маленькое изображение отображается, и отображается максимум 9 изображений. Общая структура выглядит следующим образом

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

//是否是单张图
isSingleImage:function(){
  return !(this.imageList.length>1)
},

Одна картинка показывает большую картину. Эта логика кажется простой, но правила на самом деле сложнее. Ниже приведены правила отображения одной картинки, которые я тестировал:
(1) Первое определение должно отображаться как горизонтальные или вертикальные, фиг, определяемые в соответствии с соотношением сторон оригинала
(2) Ширина всех изображений фиксирована, меняется только высота.
(3) Высота/ширина превышает определенное значение (1.8) для отображения длинной метки изображения, высота отображаемого изображения в это время в 1,45 раза больше ширины, в противном случае оно будет отображаться пропорционально
(4) Если высота/ширина меньше определенного значения (0,68), высота рамки эскиза в 0,68 раза больше ширины, и она отображается в центре, в противном случае она отображается пропорционально

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

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

Ниже приведен логический код отображения одиночного изображения.

<!--如果是单张图-->
<template v-if="isSingleImage">
    <div class="single-img-container"
         @click="showDetailImage(imageList[0])"
         :style="{backgroundImage:'url('+imageList[0]+')'}">
      <!--用div撑开图片-->
      <div class="ratio-holder" :style="{paddingTop: calcSingleImgHeight}">
      </div>
      <span class="long-image" v-if="isLongImage">长图</span>
    </div>
</template>

На картинке показано использованиеbackgroundImageатрибут без тега изображения, он выглядит проще и должен быть установленbackground-size:coverтак же какbackground-position:50%Убедитесь, что картинка расположена по центру, а div заполнен картинками, не оставляя пробелов.Конечно, картинка будет обрезана, обратите внимание наsingle-image-containerКласс устанавливает только фиксированную ширину 200 пикселей, а его высота растягивается внутренним элементом div, что даетratio-holderКласс динамически устанавливает padding-top. Процентное значение padding-top основано на процентах от ширины родительского элемента, поэтому соответствующую высоту можно вычислить по ширине. Функция вычисления высоты div, реализованная классом выше правила приведены ниже.

//单张图的高度计算
calcSingleImgHeight: function(){
    let self = this;
    let image = new Image();
    //获取图片的原始尺寸并计算比例
    //图片较大的话必须等图片加载完成才能获取尺寸
    image.onload = function(){
      self.singleImageNaturalWidth = image.naturalWidth;
      self.singleImageNaturalHeight = image.naturalHeight;
    };
    image.src = this.imageList[0];
    let ratio = this.singleImageNaturalWidth?this.singleImageNaturalHeight / this.singleImageNaturalWidth : 1;
    if(ratio < this.imageMinHeightRatio){
      ratio = this.imageMinHeightRatio
    }
    if(ratio > this.imageMaxHeightRatio){
      // 该图是长图  
      this.isLongImage = true;
      ratio = this.imageMaxHeightRatio
    }
      return ratio*100+'%';
},

Этот метод является вычисляемым свойством.Для расчета соотношения сторон изображения необходимо использовать naturalWidth и naturalHeight.Эти два значения являются исходной шириной и высотой изображения, но они должны быть получены после загрузки изображения, в противном случае он равен 0, поскольку это вычисляемое свойство, поэтому масштаб изображения пересчитывается при запуске метода onload.

Следующие анализируют дисплей миниатюр в случае нескольких изображений

<!--多张图-->
<template v-else>
    <!--图片放在backgroundImage属性中-->
    <div class="multiple-img-wrapper" :class="[colsOfMultipleImages]">
      <div class="multiple-img-container"
           v-for="(item,index) in imageList"
           @click="showDetailImage(item)"
           :style="{backgroundImage:'url('+item+')'}">
        <!--控制图片的高度和宽度一样,padding-top基于父元素的宽度-->
        <div class="ratio-holder" style="padding-top: 100%">
        </div>
        <span class="long-image" v-show="isLongImageList[index]">长图</span>
      </div>
    </div>
</template>

Правила отображения в случае нескольких изображений:1-4 листа цвет-4, 5,6 листа цвет-3, 7,8 листа цвет-4, 9 листов цвет-3,col-n представляет n столбцов

Затем используйте v-for для обхода массива изображений. Ширина и высота изображений одинаковы, поэтому padding-top равен 100%. Обратите внимание, что логика расположения изображений отличается, когда количество изображений отличается.colsOfMultipleImagesЭто вычисляемое свойство вычисляет соответствующее имя класса на основе количества изображений.

//多图时显示的列数的类
colsOfMultipleImages:function(){
  let len = this.imageList.length;
  let map = {
      	1:'col-4',2:'col-4',3:'col-4',4:'col-4',
        5:'col-3',6:'col-3',7:'col-4',8:'col-4',
        9:'col-3'};
  return len===0?'':map[len]
},

На самом деле есть только 2 класса, контролируя ширину, чтобы картинки располагались в новой строке

.col-3{
  width:75%
}
.col-4{
  width:100%;
}

Детальный анализ графика

Детальное изображение в основном связано с вращением изображения, что немного сложнее.

Подробная карта в основном разделена на 3 части: верхняя панель — это верхняя панель управления, средняя — панель отображения изображений, а нижняя — панель отображения эскизов. анализ выглядит следующим образом

(1) Если ширина изображения превышает ширину внешнего элемента div, ширина равна ширине элемента div, а высота масштабируется в соответствии с пропорциями изображения.
(2) Если ширина изображения не превышает ширину внешнего блока, изображение будет отображаться в исходном размере, а изображение будет центрировано по горизонтали.
(3) Ширина и высота загружаемого изображения по умолчанию при загрузке большого изображения фиксированы, ширина равна ширине внешнего элемента div, а высота немного меньше.

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

Нижеследующее в основном анализирует реализацию функции поворота изображения, которая немного сложна.Прежде всего, ясно, что поворот изображения здесь происходит через поворот преобразования CSS.Изображение поворачивается только визуально, но не в суть.нарисуй картинку.
Вращение CSS кажется очень простым, например, поворот на 90 градусов вправо, вам нужно только динамически установить стиль изображения.transform:rotate(90deg)OK? На самом деле, этот вид поворота приведет к тому, что изображение будет повернуто только в исходное положение (transform-origin по умолчанию имеет значение center, центральная точка изображения), а ширина и высота изображения останутся неизменными. следующая очень длинная картинка повернута по горизонтали.После направления явно есть проблема,поэтому логика тут должна бытьДинамически вычислить ширину и высоту изображения, а также ширину и высоту внешнего div, окончательный результат показан на следующей динамической диаграмме

Из приведенного выше рисунка видно, что этот вид вращения на самом деле изменяет ширину и высоту изображения.Это не простое вращение CSS.Ниже приведен медленный анализ.Структура html выглядит следующим образом.

<!--中间图片展示栏,注意需要设置高度,因为里面的img是绝对定位-->
<div class="detail-img-wrapper" :style="{height:outerDivHeight+'px'}">
    <!--加载的logo-->
    <div class="detail-img-loading">
      <circle-loading fillColor="#9C9C9C" v-if="!isDetailImageLoaded">
      </circle-loading>
    </div>
    <!--要展示的图片-->
    <img src=""
         ref="detailImage"
         v-show="isDetailImageLoaded"
         :style="detailImageStyle"
         class="detail-img">
    <!--点击隐藏详情图的div-->
    <div class="toggle-zoomout" @click="hideDetailImage">
    </div>
    <!--上一张图片的div-->
    <div class="prev-img" @click="switchImage(-1)" v-if="currentImageIndex > 0">
    </div>
    <div class="next-img" @click="switchImage(1)" v-if="currentImageIndex<imageList.length-1">
    </div>
    <!--全屏大图组件-->
    <full-screen-viewer
      :imageList="imageList"
      :currentImageIndex="currentImageIndex"
      @close="handleFullScreenViewerClose"
      v-if="isShowFullScreenViewer">
    </full-screen-viewer>
</div>

в приведенном выше коде<img>Метка представляет собой отображаемое изображение.Поскольку изображение должно быть загружено, компонент загрузки круга настроен для отображения эффекта загрузки, и при щелчке миниатюры выполняется следующая функция. Остальные div позиционируются абсолютно. Обратите внимание, что высота самого внешнего div динамически привязана. Это нужно для изменения высоты внешнего div в соответствии с изменением высоты изображения, иначе изображение переполнит div.

  //展示详情大图
  showDetailImage: function(imgUrl){
  	let self = this;
  	//设置index
        this.currentImageIndex = this.imageList.indexOf(imgUrl);
  	//改变状态为大图加载中
  	this.isDetailImageLoaded = false;
  	//计算大图的原始尺寸
        let image = this.$refs.detailImage;
        image.onload = function(){
          self.isDetailImageLoaded = true;
          self.detailImageNaturalWidth = image.naturalWidth;
          self.detailImageNaturalHeight = image.naturalHeight;
         };
        image.src = imgUrl;
        this.isShowDetail = true;
  },

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

.detail-img{
    position: absolute;
    left:50%;
    top:0;
    transform-origin: top left;
}

Приведенный выше CSS указывает на то, что изображение абсолютно позиционировано, не занимает места в документе и смещается на 50% вправо. Базовая точка преобразования устанавливается в верхнем левом углу изображения. Изображение, отображаемое таким образом, выглядит как показано ниже.

Красная точка на картинке выше — это базовая точка вращения изображения. Базовая точка находится в середине внешнего элемента div. Это облегчает обработку ряда поворотов. Нажмите кнопку поворота вправо, и изображение повернется в направление стрелки. Обратите внимание, что изображение теперь повернуто. Половина его находится за пределами div, при повороте вправо я получаю следующее изображение

На данный момент направление картинки повернуто правильно, но часть за пределами div все еще есть, так что недостаточно просто повернуть.Картинка повернута.После поворота переведите(0,-50%) требуется, то есть не обрабатывается по оси x, а расстояние смещается на -50% по оси y. Тут легко реверсировать. Картинка как будто нуждается в горизонтальном смещении. Результат поворота сейчас, поэтому текущее горизонтальное смещение является вертикальным смещением до поворота, поэтому translateY равно -50%. Если вы продолжите вращать вправо, значения translateX и translateY изменятся. Согласно выводу, массив detailImageTranslateArray можно получить. , Сохранение процента изображения, которое необходимо сместить при выполнении поворота. Следующий массив поворачивается по часовой стрелке слева направо. Первое значение каждого элемента — это значение translateX, а второе — значение translateY.

detailImageTranslateArray:[['-50%','0'],['0','-50%'],['-50%','-100%'],['-100%','-50%']],

Начальное состояние изображения — это первое значение приведенного выше массива [-50%, 0], поэтому мы можем получить значения translateX и y следующего состояния после поворота изображения в соответствии с текущими значениями translateX и y. изображения, а затем используйте это значение. Привяжите к стилю изображения, чтобы завершить поворот

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

//处理图片旋转
handleImageRotate: function(dir){
      	//图片加载完成才能旋转
      	if(!this.isDetailImageLoaded)return
        // 注意旋转中心是图片的左上角(transform-origin:top left)
        let angleDelta = dir === 1?90:-90;
        //计算旋转后的角度
        this.detailRotateAngel = (this.detailRotateAngel + angleDelta)%360;
        //修正translate的值
        let currentIndex;
        this.detailImageTranslateArray.forEach((item,index)=>{
          //找到当前的tranlate值
          if(item[0]===this.detailImageTranslateX && item[1]===this.detailImageTranslateY){
            currentIndex = index;
          }
        });
        //取下一个值
        let nextIndex = currentIndex+dir;
        if(nextIndex === this.detailImageTranslateArray.length){
          nextIndex = 0;
        }else if(nextIndex === -1){
          nextIndex = this.detailImageTranslateArray.length - 1;
        }
        //更新tranlate的值
        this.detailImageTranslateX = this.detailImageTranslateArray[nextIndex][0];
        this.detailImageTranslateY = this.detailImageTranslateArray[nextIndex][1];

        //修正外层div的高度
        this.processImageScaleInRotate();
},

Определить направление вращения по входящему параметру dir, затем вычислить угол после поворота, а затем вычислить значение, которое необходимо перевести после поворота.Поэтому поворот изображения определяется тремя значениями в данных: detailRotateAngel, detailImageTranslateX, detailImageTranslateY представляют угол поворота соответственно, смещение в направлении x, смещение в направлении y и, наконец, привязывают его к тегу img, вычисляя атрибут

//大图的style,注意旋转的时候必须重设宽高和translate值
  detailImageStyle:function(){
    return {
      width:this.detailImageWidth+'px',
      height:this.detailImageHeight+'px',
      //注意顺序:先旋转再移动
      transform:'rotate('+this.detailRotateAngel+'deg)' +' '
                +'translate('+this.detailImageTranslateX+','+this.detailImageTranslateY+')'

    }
  },

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

Ширина и высота изображения получаются из двух вычисляемых свойств.

//详情大图的高度计算
detailImageHeight:function(){
    if(!this.isDetailImageLoaded){
      //加载时高度固定
    	return this.loadingDefaultHeight
    }else{
      return this.processImageScaleInRotate().height
    }
},
//详情大图的宽度
detailImageWidth:function(){
    if(!this.isDetailImageLoaded){
      //外层div的宽度
      let outerDiv = this.$refs.wrapper;
      let clientWidth = outerDiv?outerDiv.clientWidth:1;
      return clientWidth
    }else{
      return this.processImageScaleInRotate().width
    }
},

Это разделено на расчет состояний загрузки и без загрузки.Если изображение загружается, высота фиксирована, а ширина равна ширине внешнего div.Это также фиксированное значение, установленное для эстетики.Если изображение загружается , вызываем метод processImageScaleInRotate.Рассчитываем ширину и высоту, метод следующий

//图片旋转时重新计算详情图片的宽高
processImageScaleInRotate:function(){
    //获取图片原始宽高
    let nw = this.detailImageNaturalWidth,
        nh = this.detailImageNaturalHeight;
    //根据旋转角度来计算该图是初始状态还是旋转过90度横竖交换的情况
    let angel = this.detailRotateAngel;
    //图片旋转后的宽高
    let imageRotatedWidth,imageRotatedHeight;
    let clientWidth = this.$refs.wrapper.clientWidth;
    let ratio = nh / nw;
    //是否是初始状态
    let isInitialState = true;
    if(angel === 90 || angel === 270 || angel === -90 || angel === -270){
      isInitialState = false;
    	//由初始状态旋转一次的情况
    	if(nh > clientWidth){
        imageRotatedWidth = clientWidth;
        imageRotatedHeight = imageRotatedWidth / ratio;
      }else{
        imageRotatedWidth = nh;
        imageRotatedHeight = imageRotatedWidth / ratio;
      }
    }else{
      //旋转一次变为初始状态的情况
      isInitialState = true;
      if(nw > clientWidth){
        imageRotatedWidth = clientWidth;
        imageRotatedHeight = imageRotatedWidth * ratio;
      }else{
        imageRotatedWidth = nw;
        imageRotatedHeight = imageRotatedWidth * ratio;
      }
    }
    //注意这里的判断,width和height在旋转状态下容易弄反
    return {
    	width:isInitialState?imageRotatedWidth:imageRotatedHeight,
        height:isInitialState?imageRotatedHeight:imageRotatedWidth
    }
},

Этот метод более сложный.Мы можем обнаружить, что независимо от того, как он повернут, данные [ширина, высота] изображения могут существовать только в двух состояниях, исходном состоянии и состоянии после одного поворота.Это легко понять. Сделайте снимок и оперируйте его.Понятно, что эти два состояния определяются углом поворота detailRotateAngel картинки.При угле 90, 270, -90, -270 градусов это состояние после одного поворота, иначе является начальным состоянием, а затем рассчитывается для этих двух состояний соответственно ширина и высота

Вычисление начального состояния: Сначала получите исходную ширину и высоту nw, nh изображения, а затем вычислите его соотношение.Когда nw>clientWidth, это указывает на то, что исходная ширина изображения больше, чем максимальная ширина внешнего div, а ширина изображения равна clientWidth, в противном случае ширина равна его собственной исходной ширине, а высота получается путем умножения соотношения на ширину. Это гарантирует, что ширина изображения не превышает ширину div.

Вычисление после одного поворота из начального состояния: принцип такой же, как и выше, за исключением того, что в это время оценивается значение nh (исходная высота), поскольку ширина и высота изображения заменяются

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

 <div class="detail-img-wrapper" :style="{height:outerDivHeight+'px'}">
 <div>

Мы динамически привязываем высоту к внешнему div, externalDivHeight — это вычисляемое свойство.

//外层div的高度(随着图片旋转而变化)
outerDivHeight: function(){
    //根据旋转角度来计算该图是初始状态还是旋转过90度横竖交换的情况
    let angel = this.detailRotateAngel;
    if(angel === 90 || angel === 270 || angel === -90 || angel === -270) {
      //由初始状态旋转一次的情况
    	return this.detailImageWidth
    }else{
    	//初始状态
      return this.detailImageHeight
    }
}

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

Это длинная картина

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

//计算每张图是否是长图
calcImageIsLongImage: function(){
    let self = this;
    //计算每张图是否是长图
    this.imageList.forEach((item,index)=>{
      let image = new Image();
      image.onload = function(){
        let ratio = image.naturalHeight / image.naturalWidth;
        if(ratio > self.longImageLimitRatio){
        	//通过$set方法修改数组中的值
          self.$set(self.isLongImageList,index,true)
        }
      };
      image.src = item;
    })
},

Этот метод вызывается при монтировании, проходит через массив URL-адресов изображений, переданный реквизитом, затем создает новое изображение для каждого изображения и получает его соотношение сторон при загрузке.Если оно больше порогового значения, установите элемент в массиве длинных изображений. isLongImageList в true, наконец, он абсолютно позиционируется в div изображения с помощью тега span.
<span class="long-image" v-show="isLongImageList[index]">长图</span>

Полноэкранный компонент большого изображения

Этот компонент относительно прост. Компонент фиксирован и позиционирован. CSS внешнего div выглядит следующим образом, ширина и высота — полноэкранные, а z-индекс — максимально большой.

  position: fixed;
  left:0;
  top:0;
  width:100vw;
  height:100vh;
  z-index:10000;

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

Как это сделать? Просто установите следующий css в тег img

.img{
  max-width: 100vw;
  max-height: 100vh;
}

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

.img{
    max-height: none;
}

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