乐趣区

关于前端:作兔器手写一个可爱的兔兔相册展示器

作兔器——手写一个可恶的兔兔相册展现器

兔岁既来,为迎兔岁,更认为兔兔甚是可怜,故作一兔相册展现器,以藏兔图。而今先顾此效,如下所示:

自译: 兔年既然来了,为了示意欢送兔年的到来,而且我也认为兔兔很是可恶,令人喜爱,因而写了一个兔兔相册展现器,用来珍藏可恶的兔兔图片,当初让咱们先来看看这个相册展现器的成果吧,正如上面所展现的:

据上而知,此器可分三成,一则为头部,头部含一按钮,点此可随意见兔之图,二则为图区,图区不限,此例乃九,每一区见一兔,鼠标移之可得一词以述兔也,所述则同分为二,其一乃题,其二乃文,文即一词。

自译: 依据上图咱们能够晓得,这个兔兔相册展现器能够分成三个局部,第一个局部就是一个标题栏头部,标题栏头部有一个按钮,点击这个按钮能够随机的展示兔兔的图片,第二个局部则是兔兔的图片展现区域,这个展现区域能够分成多个局部,在这个例子当中是九个图区,每一个图区就会展现一个兔兔,鼠标移上去能够看到一首词来形容兔兔。这个形容同样分成了两局部,第一局部就是题目,第二局部就是内容,内容就是一首诗。

三则为细品兔图, 鼠标击之,可得一黑盒,黑盒中位,即为兔图,鼠标复击之,即可关之。由之可见,其有动效,如何实之?需有二者,其一为定时器,其二乃以透明度为效,如是娓娓道来。

自译:第三个局部则是具体的预览图片,通过应用鼠标点击图片,就能够失去一个彩色的背景遮罩,在彩色背景遮罩的两头地位,就是兔兔的图片,鼠标再次点击就能够敞开具体预览。从这里咱们能够看到,这个敞开和关上都是有动画成果的,那么咱们如何实现这个动画成果呢?第一局部很显然咱们须要用到定时器,第二局部就是通过批改透明度达到成果,接下来让咱们一一来看每一部分的实现吧。

其一,构之 HTML 元素:

自译:第一步,当然是编写 html 元素。

<div class="rabbit-box">
  <header class="rabbit-box-header">
    <button type="button" class="rabbit-box-header-btn"> 换一换 </button>
  </header>
  <div class="rabbit-box-album"></div>
</div>

如上所示,应知外有容器元素,内有题目元素与兔图容器元素,其内更有一按钮元素,使之可更兔图,兔图容器之内,乃元素抽成,如之奈何?

自译: 正如下面所展现的,咱们应该晓得最外层有一个容器元素, 外面又有一个题目元素以及一个装满兔子图片的容器元素,在题目元素中还有一个按钮元素,能够让它来随机更换兔子图片,兔子图片外部的元素正好是动静生成的元素,咱们应该怎么做呢?

使我等估之如下:

自译: 让咱们猜想 html 元素构造应该如下所示:

<div class="rabbit-box-album-item" data-title="..." data-content="...">
  <img src="..." alt="图片加载中" class="rabbit-box-album-item-img" />
</div>

甚易,构之大成,因此画之美样,但其乃易,故可径直赏码矣。

自译: 太简略了,构建元素功败垂成,因而咱们只须要用 css 款式丑化一下即可,不过因为这也同样简略,因而咱们只须要间接查看代码即可。

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body,
html {
  height: 100%;
  overflow: hidden;
}
body {
  overflow-y: auto;
  padding: 2em;
  background: linear-gradient(135deg, #f184e8 10%, #e209e9 90%);
}
.rabbit-box {
  width: 100%;
  max-width: 1200px;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  margin: auto;
}

.rabbit-box-header {
  width: 100%;
  background: linear-gradient(135deg, #ffc5e7 10%, #fa6cc4 90%);
  height: 60px;
  border-radius: 10px 10px 0 0;
  text-align: center;
  line-height: 60px;
}

.rabbit-box-header-btn {
  outline: none;
  border-radius: 4px;
  padding: 0.4rem 1rem;
  border: none;
  transition: all 0.4s cubic-bezier(0.075, 0.82, 0.165, 1);
  display: inline-block;
  color: #fff;
  cursor: pointer;
  background: linear-gradient(135deg, #f173bd 10%, #e71b92 90%);
  letter-spacing: 2px;
  font-size: 20px;
  font-family: '微软雅黑', '楷体';
}

.rabbit-box-header-btn:hover,
.rabbit-box-header-btn:active {background: linear-gradient(135deg, #f060b4 10%, #e60f8c 90%);
}

.rabbit-box-album {
  width: 100%;
  height: 100%;
  display: flex;
  flex-wrap: wrap;
}

.rabbit-box-album-item {
  width: 33.3333%;
  height: 300px;
  overflow: hidden;
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.5);
  cursor: pointer;
  position: relative;
}

.rabbit-box-album-item::before,
.rabbit-box-album-item::after {
  position: absolute;
  color: #fff;
  font-size: 15px;
  width: 100%;
  height: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
  left: 0;
  transition: transform 0.4s ease-in-out;
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}

.rabbit-box-album-item::before {content: attr(data-title);
  top: 0;
  background-color: rgba(0, 0, 0, 0.85);
  transform: translateY(-100%) scale(0);
}
.rabbit-box-album-item::after {content: attr(data-content);
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.65);
  border-top: 1px solid #fff;
  transform: translateY(100%) scale(0);
}

.rabbit-box-album-item:hover.rabbit-box-album-item::before,
.rabbit-box-album-item:hover.rabbit-box-album-item::after {transform: translateY(0) scale(1);
}
.rabbit-box-album-item-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
@media screen and (max-width: 900px) {
  .rabbit-box-album-item {
    width: 100%;
    height: 200px;
  }
}

::-webkit-scrollbar {
  width: 4px;
  height: 4px;
  background-color: #f1f1f1;
}

::-webkit-scrollbar-thumb {
  width: 4px;
  height: 4px;
  background-color: #ee4f9e;
}

甚易,皆凡样也,故无需多言以述之,使我等望功码。

自译: 太简略了,都是一些惯例的布局款式,因而没有必要说太多来解释,让咱们来看性能代码的实现吧。

其一,有一构造函数,函数一对参,参内二属,一为 resources, 二为 el,resources 乃对组,对内有三属,分为 src,title,content, 顾名思义,src 为兔图之源,title 为题目,content 为内容,el 则为容器元素,值乃字符,顾可得如下:

自译: 首先咱们会创立一个构造函数,构造函数有一个对象参数,对象参数外面是二个属性,第一个是 resources, 第二个则是 el,resources 为一个对象数组,对象外面别离有三个属性,别离是 src,title,content, 从名字就能想到其中的含意,src 代表兔兔图片的门路,title 就是题目,content 就是内容,el 则代表是容器元素,是一个字符串,因而咱们就能够失去如下代码:

const resources = [
    {
        src:"./images/rabbit-1.png",
        title:"可恶的兔兔 -1",
        content:"雄兔脚扑朔,雌兔眼迷离。"
    },
    {
        src:"./images/rabbit-2.png",
        title:"可恶的兔兔 -2",
        content:"兔走乌驰人语静,满溪红袂棹歌初。"
    },
    {
        src:"./images/rabbit-3.png",
        title:"可恶的兔兔 -3",
        content:"白兔捣药成,问言与谁餐?"
    },
    {
        src:"./images/rabbit-4.png",
        title:"可恶的兔兔 -4",
        content:"兔丝附蓬麻,引蔓故不长。"
    },
    {
        src:"./images/rabbit-5.png",
        title:"可恶的兔兔 -5",
        content:"白兔捣药秋复春,嫦娥孤栖与谁邻。"
    },
    {
        src:"./images/rabbit-6.png",
        title:"可恶的兔兔 -6",
        content:"吴质不眠倚桂树,露脚斜飞湿寒兔。"
    },
    {
        src:"./images/rabbit-7.png",
        title:"可恶的兔兔 -7",
        content:"茕茕白兔,东走西顾。"
    },
    {
        src:"./images/rabbit-8.png",
        title:"可恶的兔兔 -8",
        content:"可笑常娥不了事,走却玉兔来世间。"
    },
    {
        src:"./images/rabbit-9.png",
        title:"可恶的兔兔 -9",
        content:"兔走乌驰人语静,满溪红袂棹歌初。"
    }
];
const rabbit = new RabbitAlbum({
    resources: ,
    el:".rabbit-box-album"
})

使我等见构造函数如下:

自译: 让咱们来看看构造函数的实现如下:

class RabbitAlbum {constructor(options) {this.animation = Object.create(null);
    this.initAnimation();
    this.container = RabbitAlbum.$(options.el) || document.body;
    this.imageList = options.resources || [];
    this.createAlbum();}
}

如上初始所属,内有二方,其一为 initAnimation, 意即初始动效, 其二为 createAlbum, 意即生兔图也,三属分为 animation,container,imgList,即动效, 容器元素, 兔图对组。

自译: 以上咱们初始化所有的属性,外面有二个办法,第一个办法就是 initAnimation,意思就是初始化动画,第二个办法就是 createAlbum, 意思就是生成兔子图片,三个属性别离是 animation,container,imgList, 也就是动画成果,容器元素,兔兔图片对象数组。

有一 $, 乃是静方,使我等见之如下:

自译: 有一个 $ 办法,就是一个静态方法,让咱们来看看如下所示:

RabbitAlbum.$ = (selector, el = document) => el.querySelector(selector);

甚易, 其乃 document.querySelector 方包耳!。另有二静方,为 getCss,\_create, 使我等见之如下:

自译: 太简略了,就是 document.querySelector 办法的包装罢了。另外还有二个静态方法,那就是 getCss,\_create, 让咱们来看看如下所示:

RabbitAlbum.getCss = (el, prop) => window.getComputedStyle(el, null)[prop];
RabbitAlbum._create = name => document.createElement(name);

getCss 为 window.getComputedStyle 方包耳!,\_create 为 document.createElement 方包耳! 甚易,如视 createAlbum 方,后见 initAnimation 方。

自译: getCss 就是 window.getComputedStyle 办法的包装罢了,\_create 就是 document.createElement 办法的包装罢了, 接下来咱们先来看 createAlbum 办法,最初再看 initAnimation 的实现。

createAlbum(){const randomSort = this.imageList.sort((a,b) => Math.random() > 0.5 ? -1 : 1);
    let template = '';
    randomSort.forEach(item => {
        template += `
            <div class="rabbit-box-album-item" data-title="${item.title}" data-content="${item.content}">
                <img src="${item.src}" alt="图片加载中" class="rabbit-box-album-item-img">
            </div>
        `;
    });
    this.container.innerHTML = template;
    this.container.addEventListener('click',e => {if(e.target.className.includes('rabbit-box-album-item')){if(e.target.firstElementChild){const src = e.target.firstElementChild.getAttribute('src');
                this.preview(src);
            }
        }
    })
}

其一,乱序兔图对组,编之生兔图元素,入 container 为子元素,为 container 元素增击事,断之乃图元素,而取 src 属,调 preview 方也。

自译: 首先将兔图对象数组打乱程序,遍历这个数组生成兔图元素,增加到 container 容器元素中作为子元素,而后给 container 元素增加点击事件,判断是否是图片元素,而后获取 src 属性,调用 preview 办法。

使我等见 preview 方如下:

自译: 让咱们来看看 preview 办法如下:

preview(src,time = 600){const imgTag = RabbitAlbum._create('img'),
          imgMask = RabbitAlbum._create('div');
    imgMask.style.cssText += 'position:fixed;left:0;top:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:9999;';
    imgTag.style.cssText += 'position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:auto;height:auto;border-radius:5px;';
    imgMask.id = 'previewMask';
    imgTag.src = src;
    imgMask.appendChild(imgTag);
    const mask = RabbitAlbum.$("#previewMask");
    if (!mask) {document.body.appendChild(imgMask);
        this.animation.fadeIn(imgMask, time);
    } else {document.body.replaceChild(imgMask, mask);
        this.animation.fadeIn(imgMask, time);
    }
    imgMask.addEventListener('click',  e => {const el = e.target.tagName.toLowerCase().indexOf('img') > -1 ? e.target.parentElement : e.target;
        this.animation.fadeOut(el, time);
    }, false)
}

其毕生 img 元素与 div 元素,分为增样,后入 body 元素,调 fadeIn 方见之,为 div 元素增击事,因此隐之。

自译: 第一步生成 img 和 div 元素,别离增加款式,而后增加到 body 元素中,而后调用 fadeIn 办法显示它们,并且为 div 元素增加点击事件,从而达到暗藏它们。

何知 fadeIn 方与 fadeOut 方焉? 如之奈何?使我等观之。其毕生时管器,如下:

自译: 是不是有些相熟 fadeIn 办法和 fadeOut 办法? 如何实现呢? 让咱们一起来看看吧,首先是须要创立一个工夫管理器,如下:

class TimerManager {constructor() {this.timers = [];
    this.args = [];
    this.isTimerRun = false;
  }
  add(timer, args) {this.timers.push(timer);
    this.args.push(args);
    this.timerRun();}
  timerRun() {if (!this.isTimerRun) {const timer = this.timers.shift(),
        args = this.args.shift();
      if (timer && args) {
        this.isTimerRun = true;
        timer(args[0], args[1]);
      }
    }
  }
  next() {
    this.isTimerRun = false;
    this.timerRun();}
}
TimerManager.makeTimerManage = element => {
  if (
    !element.TimerManage ||
    element.TimerManage.constructor !== TimerManager
  ) {element.TimerManage = new TimerManager();
  }
};
TimerManager.runNext = element => {if (element.TimerManage && element.TimerManage.constructor === TimerManager) {element.TimerManage.next();
  }
};

其效如何? 生组而存定时器,后一一取出调之,甚易,使我等观 fadeIn 方与 fadeOut 方, 二者所似,知其一便知其二也,皆以定时器增减 opacity 矣。

自译: 这个工夫管理器的用途是什么呢?也就是创立一个数组存储定时器,而后每次都从这个数组当中取出一个,而后调用它,这很简略,让咱们来说看看 fadeIn 与 fadeOut 办法的实现。两个办法的实现原理都很相似,晓得其中一个办法的实现便晓得另一个办法的实现,它们都是通过定时器减少或者是缩小 opacity 即可。

fadeIn(element,time){
    element.style.transition = "opacity" + time + "ms";
    if (!Number(RabbitAlbum.getCss(element, 'opacity')) || !parseInt(RabbitAlbum.getCss(element, 'opacity')) <= 0) {
        element.style.display = "none";
        element.style.opacity = 0;
        let curAlpha = 0,
            addAlpha = 1 * 100 / (time / 10),
            timer = null;
        let handleFade = function () {
            curAlpha += addAlpha;
            if (element.style.display === 'none'){element.style.display = "block";}
            element.style.opacity = (curAlpha / 100).toFixed(2);
            if (curAlpha >= 100) {if (timer) clearTimeout(timer);
                element.style.opacity = 1;
                TimerManager.runNext(element);
            } else {timer = setTimeout(handleFade, 10);
            }
        }
        handleFade();} else {TimerManager.runNext(element);
    }
}

甚易,后使我等观 initAnimation 方,如下:

很简略,最初让咱们来看看 initAnimation 办法 的实现,如下所示:

initAnimation(){
    const _self = this;
    if(typeof this.animation['fadeIn'] !== 'function'){this.animation['fadeIn'] = function(element,time = 400){TimerManager.makeTimerManage(element);
            element.TimerManage.add(_self.fadeIn, arguments);
            return this;
        }
    }
    if(typeof this.animation['fadeOut'] !== 'function'){this.animation['fadeOut'] = function(element,time = 400){TimerManager.makeTimerManage(element);
            element.TimerManage.add(_self.fadeOut, arguments);
            return this;
        }
    }
}

甚易,初始 fadeIn 方与 fadeOut 方,后入元素矣。

自译: 很简略,就是初始化 fadeIn 和 fadeOut 办法,把它们增加到元素当中就行了。

综上,可得作兔器也,使我等观之。

自译: 综上所述,咱们的兔兔相册管理器就实现了,让咱们来看看最初的成果吧。

在线示例

退出移动版