UGUI轮播图组件实现方法详解

本文实例为大家分享了UGUI轮播图组件实现的具体代码,供大家参考,具体内容如下

要用到,于是就自已做了一个,自认为封装上还是OK的,开发于unity5.1.2。

支持自动轮播、手势切换、代码调用切换,支持水平和竖直两个方向以及正负方向轮播,轮播索引改变有回调可以用,也可以获取到当前处于正中的子元素。

要注意的是,向轮播列表中加入新元素不能直接setparent,要调用该组件的AddChild方法

下面是鄙人的代码:

/// 主要关注属性、事件及函数:
///  public int CurrentIndex;
///  public Action<int> OnIndexChange;
///  public virtual void MoveToIndex(int ind);
///  public virtual void AddChild(RectTransform t);
/// by yangxun
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;
/// <summary>
/// 轮播图组件
/// </summary>
[RequireComponent(typeof(RectTransform)), ExecuteInEditMode]
public class Carousel : UIBehaviour, IEventSystemHandler, IBeginDragHandler, IInitializePotentialDragHandler, IDragHandler, IEndDragHandler, ICanvasElement {

 /// <summary>
 /// 子物体size
 /// </summary>
 public Vector2 CellSize;
 /// <summary>
 /// 子物体间隔
 /// </summary>
 public Vector2 Spacing;
 /// <summary>
 /// 方向
 /// </summary>
 public Axis MoveAxis;
 /// <summary>
 /// Tween时的步数
 /// </summary>
 public int TweenStepCount = 10;
 /// <summary>
 /// 自动轮播
 /// </summary>
 public bool AutoLoop = false;
 /// <summary>
 /// 轮播间隔
 /// </summary>
 public float LoopSpace = 1;
 /// <summary>
 /// 轮播方向--1为向左移动,-1为向右移动
 /// </summary>
 public int LoopDir = 1;
 /// <summary>
 /// 可否拖动
 /// </summary>
 public bool Drag = true;
 /// <summary>
 /// 位于正中的子元素变化的事件,参数为index
 /// </summary>
 public Action<int> OnIndexChange;
 /// <summary>
 /// 当前处于正中的元素
 /// </summary>
 public int CurrentIndex {
  get {
   return m_index;
  }
 }

 private bool m_Dragging = false;
 private bool m_IsNormalizing = false;
 private Vector2 m_CurrentPos;
 private int m_currentStep = 0;
 private RectTransform viewRectTran;
 private Vector2 m_PrePos;
 private int m_index = 0,m_preIndex = 0;
 private RectTransform header;
 private bool contentCheckCache = true;

 private float currTimeDelta = 0;
 private float viewRectXMin {
  get{
   Vector3[] v = new Vector3[4];
   viewRectTran.GetWorldCorners(v);
   return v[0].x;
  }
 }
 private float viewRectXMax {
  get {
   Vector3[] v = new Vector3[4];
   viewRectTran.GetWorldCorners(v);
   return v[3].x;
  }
 }
 private float viewRectYMin {
  get {
   Vector3[] v = new Vector3[4];
   viewRectTran.GetWorldCorners(v);
   return v[0].y;
  }
 }
 private float viewRectYMax {
  get {
   Vector3[] v = new Vector3[4];
   viewRectTran.GetWorldCorners(v);
   return v[2].y;
  }
 }

 public int CellCount {
  get {
   return transform.childCount;
  }
 }
 protected override void Awake() {
  base.Awake();
  viewRectTran = GetComponent<RectTransform>();
  header = GetChild(viewRectTran, 0);
 }
 public void resizeChildren() {
  //init child size and pos
  Vector2 delta;
  if (MoveAxis == Axis.Horizontal) {
   delta = new Vector2(CellSize.x + Spacing.x, 0);
  }
  else {
   delta = new Vector2(0, CellSize.y + Spacing.y);
  }
  for (int i = 0; i < CellCount; i++) {
   var t = GetChild(viewRectTran, i);
   if (t) {
    t.localPosition = delta * i;
    t.sizeDelta = CellSize;
   }
  }
  m_IsNormalizing = false;
  m_CurrentPos = Vector2.zero;
  m_currentStep = 0;
 }
 /// <summary>
 /// 加子物体到当前列表的最后面
 /// </summary>
 /// <param name="t"></param>
 public virtual void AddChild(RectTransform t) {
  if (t!=null) {
   t.SetParent(viewRectTran, false);
   t.SetAsLastSibling();
   Vector2 delta;
   if (MoveAxis == Axis.Horizontal) {
    delta = new Vector2(CellSize.x + Spacing.x, 0);
   }
   else {
    delta = new Vector2(0, CellSize.y + Spacing.y);
   }
   if (CellCount == 0) {
    t.localPosition = Vector3.zero;
    header = t;
   }
   else {
    t.localPosition = delta + (Vector2)GetChild(viewRectTran,CellCount-1).localPosition;
   }
  }
 }
 protected override void OnEnable() {
  base.OnEnable();
  resizeChildren();
  return;
  if (Application.isPlaying) {
   if (ContentIsLongerThanRect()) {
    int s;
    do {
     s = GetBoundaryState();
     LoopCell(s);
    } while (s != 0);
   }
  }
 }
 protected virtual void Update() {
  if (ContentIsLongerThanRect()) {
   //实现在必要时loop子元素
   if (Application.isPlaying) {
    int s = GetBoundaryState();
    LoopCell(s);
   }
   //缓动回指定位置
   if (m_IsNormalizing && EnsureListCanAdjust()) {
    if (m_currentStep == TweenStepCount) {
     m_IsNormalizing = false;
     m_currentStep = 0;
     m_CurrentPos = Vector2.zero;
     return;
    }
    Vector2 delta = m_CurrentPos/TweenStepCount;
    m_currentStep++;
    TweenToCorrect(-delta);
   }
   //自动loop
   if (AutoLoop && !m_IsNormalizing && EnsureListCanAdjust()) {
    currTimeDelta += Time.deltaTime;
    if (currTimeDelta>LoopSpace) {
     currTimeDelta = 0;
     MoveToIndex(m_index + LoopDir);
    }
   }
   //检测index是否变化
   if (MoveAxis == Axis.Horizontal) {
    m_index = (int)(header.localPosition.x / (CellSize.x + Spacing.x-1));
   }
   else {
    m_index = (int)(header.localPosition.y / (CellSize.y + Spacing.y-1));
   }
   if (m_index<=0) {
    m_index = Mathf.Abs(m_index);
   }
   else {
    m_index = CellCount - m_index;
   }
   if (m_index != m_preIndex) {
    if (OnIndexChange != null) {
     OnIndexChange(m_index);
    }
   }
   m_preIndex = m_index;
  }
 }
 public virtual void OnBeginDrag(PointerEventData eventData) {
  if (!Drag || !contentCheckCache) {
   return;
  }
  Vector2 vector;
  if (((eventData.button == PointerEventData.InputButton.Left) && this.IsActive()) && RectTransformUtility.ScreenPointToLocalPointInRectangle(this.viewRectTran, eventData.position, eventData.pressEventCamera, out vector)) {
   this.m_Dragging = true;
   m_PrePos = vector;
  }
 }

 public virtual void OnInitializePotentialDrag(PointerEventData eventData) {
  if (!Drag) {
   return;
  }
  return;
 }

 public virtual void OnDrag(PointerEventData eventData) {
  if (!Drag || !contentCheckCache) {
   return;
  }
  Vector2 vector;
  if (((eventData.button == PointerEventData.InputButton.Left) && this.IsActive()) && RectTransformUtility.ScreenPointToLocalPointInRectangle(this.viewRectTran, eventData.position, eventData.pressEventCamera, out vector)) {
   m_IsNormalizing = false;
   m_CurrentPos = Vector2.zero;
   m_currentStep = 0;
   Vector2 vector2 = vector - this.m_PrePos;
   Vector2 vec = CalculateOffset(vector2);
   this.SetContentPosition(vec);
   m_PrePos = vector;
  }
 }
 /// <summary>
 /// 移动到指定索引
 /// </summary>
 /// <param name="ind"></param>
 public virtual void MoveToIndex(int ind) {
  if (m_IsNormalizing) {
   return;
  }
  //Debug.LogFormat("{0}->{1}",m_index,ind);
  if (ind == m_index) {
   return;
  }
  this.m_IsNormalizing = true;
  Vector2 offset;
  if (MoveAxis == Axis.Horizontal) {
   offset = new Vector2(CellSize.x + Spacing.x, 0);
  }
  else {
   offset = new Vector2(0, CellSize.y + Spacing.y);
  }
  var delta = CalcCorrectDeltaPos();
  int vindex = m_index;
  m_CurrentPos = delta + offset * (ind - vindex);
  //m_CurrentPos = -(Vector2)header.localPosition + offset * (ind - m_index);
  m_currentStep = 0;
 }
 private Vector2 CalculateOffset(Vector2 delta) {
  if (MoveAxis == Axis.Horizontal) {
   delta.y = 0;
  }
  else {
   delta.x = 0;
  }
  return delta;
 }
 private void SetContentPosition(Vector2 position) {
  foreach (RectTransform i in viewRectTran) {
   i.localPosition += (Vector3)position;
  }
  return;
 }

 public virtual void OnEndDrag(PointerEventData eventData) {
  if (!Drag || !contentCheckCache) {
   return;
  }
  this.m_Dragging = false;
  this.m_IsNormalizing = true;
  m_CurrentPos = CalcCorrectDeltaPos();
  m_currentStep = 0;
 }

 public virtual void Rebuild(CanvasUpdate executing) {
  return;
 }
 /// <summary>
 /// List是否处于可自由调整状态
 /// </summary>
 /// <returns></returns>
 public virtual bool EnsureListCanAdjust() {
  return !m_Dragging && ContentIsLongerThanRect();
 }
 /// <summary>
 /// 内容是否比显示范围大
 /// </summary>
 /// <returns></returns>
 public virtual bool ContentIsLongerThanRect() {
  float contentLen;
  float rectLen;
  if (MoveAxis == Axis.Horizontal) {
   contentLen = CellCount*(CellSize.x + Spacing.x) - Spacing.x;
   rectLen = viewRectTran.rect.xMax - viewRectTran.rect.xMin;
  }
  else {
   contentLen = CellCount * (CellSize.y + Spacing.y) - Spacing.y;
   rectLen = viewRectTran.rect.yMax - viewRectTran.rect.yMin;
  }
  contentCheckCache = contentLen > rectLen;
  return contentCheckCache;
 }
 /// <summary>
 /// 检测边界情况,分为0未触界,-1左(下)触界,1右(上)触界
 /// </summary>
 /// <returns></returns>
 public virtual int GetBoundaryState() {
  RectTransform left;
  RectTransform right;
  left = GetChild(viewRectTran, 0);
  right = GetChild(viewRectTran, CellCount - 1);
  Vector3[] l = new Vector3[4];
  left.GetWorldCorners(l);
  Vector3[] r = new Vector3[4];
  right.GetWorldCorners(r);
  if (MoveAxis == Axis.Horizontal) {
   if (l[0].x>=viewRectXMin) {
    return -1;
   }
   else if (r[3].x < viewRectXMax) {
    return 1;
   }
  }
  else {
   if (l[0].y >= viewRectYMin) {
    return -1;
   }
   else if (r[1].y < viewRectYMax) {
    return 1;
   }
  }
  return 0;
 }
 /// <summary>
 /// Loop列表,分为-1把最右(上)边一个移到最左(下)边,1把最左(下)边一个移到最右(上)边
 /// </summary>
 /// <param name="dir"></param>
 protected virtual void LoopCell(int dir) {
  if (dir == 0) {
   return;
  }
  RectTransform MoveCell;
  RectTransform Tarborder;
  Vector2 TarPos;
  if (dir == 1) {
   MoveCell = GetChild(viewRectTran, 0);
   Tarborder = GetChild(viewRectTran, CellCount - 1);
   MoveCell.SetSiblingIndex(CellCount-1);
  }
  else {
   Tarborder = GetChild(viewRectTran, 0);
   MoveCell = GetChild(viewRectTran, CellCount - 1);
   MoveCell.SetSiblingIndex(0);
  }
  if (MoveAxis == Axis.Horizontal) {
   TarPos = Tarborder.localPosition + new Vector3((CellSize.x + Spacing.x) * dir, 0,0);
  }
  else {
   TarPos = (Vector2)Tarborder.localPosition + new Vector2(0, (CellSize.y + Spacing.y) * dir);
  }
  MoveCell.localPosition = TarPos;
 }
 /// <summary>
 /// 计算一个最近的正确位置
 /// </summary>
 /// <returns></returns>
 public virtual Vector2 CalcCorrectDeltaPos() {
  Vector2 delta = Vector2.zero;
  float distance = float.MaxValue;
  foreach (RectTransform i in viewRectTran) {
   var td = Mathf.Abs(i.localPosition.x) + Mathf.Abs(i.localPosition.y);
   if (td<=distance) {
    distance = td;
    delta = i.localPosition;
   }
   else {
    break;
   }
  }
  return delta;
 }
 /// <summary>
 /// 移动指定增量
 /// </summary>
 protected virtual void TweenToCorrect(Vector2 delta) {
  foreach (RectTransform i in viewRectTran) {
   i.localPosition += (Vector3)delta;
  }
 }
 public enum Axis {
  Horizontal,
  Vertical
 }
 private static RectTransform GetChild(RectTransform parent, int index) {
  if (parent == null||index>=parent.childCount) {
   return null;
  }
  return parent.GetChild(index) as RectTransform;
 }
}

用法和ugui的scrollrect组件是差不多的,因为本来在drag事件上有所借鉴
例图如下:

另外,它不会像ugui的几个布局组件一样自动去改变子元素的大小为cellsize,cellsize只是虚拟的子元素容器大小,这个要注意下。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Unity实现图片轮播组件

    游戏中有时候会见到图片轮播的效果,那么这里就自己封装了一个,包括自动轮播.切页按钮控制.页码下标更新.滑动轮播.切页后的回调等等 . 下面,先上一个简陋的gif动态效果图 从图中可以看出,该示例包括了三张图片的轮播,左右分别是上一张和下一张的按钮,右下角显示了当前是第几章的页码下标. 直接上脚本: using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using U

  • UGUI轮播图组件实现方法详解

    本文实例为大家分享了UGUI轮播图组件实现的具体代码,供大家参考,具体内容如下 要用到,于是就自已做了一个,自认为封装上还是OK的,开发于unity5.1.2. 支持自动轮播.手势切换.代码调用切换,支持水平和竖直两个方向以及正负方向轮播,轮播索引改变有回调可以用,也可以获取到当前处于正中的子元素. 要注意的是,向轮播列表中加入新元素不能直接setparent,要调用该组件的AddChild方法 下面是鄙人的代码: /// 主要关注属性.事件及函数: /// public int Current

  • Vue使用Swiper封装轮播图组件的方法详解

    目录 Swiper 为什么要封装组件 开始封装 1.下载安装Swiper 2.引入css样式文件 3.引入js文件 4.把官网使用方法中的HTML结构复制粘贴过来 5.初始化Swiper 自定义效果 完整代码 效果展示 Swiper Swiper是一个很常用的用于实现各种滑动效果的插件,PC端和移动端都能很好的适配. 官网地址:www.swiper.com.cn/ 目前最新版本是Swiper7,但众所周知最新版本通常不稳定,所以这里使用Swiper6来封装. Swiper各版本区别: 为什么要封

  • 微信小程序swiper轮播图组件使用方法详解

    本文实例为大家分享了微信小程序swiper轮播图组件的使用,供大家参考,具体内容如下 在components中新建文件夹swiper components/swiper/swiper.wxml <!--components/swiper/swiper.wxml--> <view class="container">     <swiper class="swiper-box" bind:change="swiperChange

  • vue.js轮播图组件使用方法详解

    之前用jQuery写过轮播组件,用的jquery动画实现的图片滑动效果.这个组件的滑动特效是原生js搭配vue的数据绑定实现的,不依赖其他库,虽然可以再vue.js中引入swiper,但是引入类库的最大的缺点就是冗余代码太多,所以还是自己写一个比较好,简单扼要.(ps:组件的宽高设置还有有点小bug,子组件中需要改为用js动态修改container的宽高,另外可能还有其他地方有不合理之处,欢迎各位批评指正) github地址:git@github.com:cainiao222/vueslider

  • jQuery轮播图功能制作方法详解

    本文实例讲述了jQuery轮播图功能制作方法.分享给大家供大家参考,具体如下: 在写轮播图之前我们先看看这个轮播图完成后的样式是怎样的 素材图片 : 代码 HTML代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-w

  • jquery轮播图插件使用方法详解

    本文实例为大家分享了jquery轮播图插件使用案例,供大家参考,具体内容如下 代码 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <link rel="stylesheet" type="text/css" href="style.css" /> <

  • Vue的轮播图组件实现方法

    今天在上慕课老师fishenal的vue实战课程的时候,有一个轮播图组件实现,在跟着做的时候,自己也踩了一些坑.此外,在原课程案例的基础上,我加入了不同方向的滑动功能. 本文章采用Vue结合Css3来实现轮播图. 首先要了解的是Vue的动画原理.在vue中,如果我们要给元素设置动画效果,则需要使用一个<transition name="targetClassName"></transition>将相应的元素包裹住,如下: <transition name=

  • vue3封装轮播图组件的方法

    目的 封装轮播图组件,直接使用,具体内容如下 大致步骤 准备my-carousel组件基础布局,全局注册 准备home-banner组件,使用my-carousel组件,再首页注册使用. 深度作用选择器覆盖my-carousel组件的默认样式 在home-banner组件获取轮播图数据,传递给my-carousel组件 在my-carousel组件完成渲染 自动播放,暴露自动轮播属性,设置了就自动轮播 如果有自动播放,鼠标进入离开,暂停,开启 指示器切换,上一张,下一张 销毁组件,清理定时器 落

  • vue利用better-scroll实现轮播图与页面滚动详解

    前言 better-scroll 也很强大,不仅可以做普通的滚动列表,还可以做轮播图.picker 等等...所以本文主要给大家介绍了关于vue用better-scroll实现轮播图与页面滚动的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1.安装better-scroll 在根目录中package.json的dependencies中添加: "better-scroll": "^0.1.15" 然后 npm i 安装. 2.封装代码

  • 微信小程序滚动、轮播图和文本实例详解

    目录 小程序的宿主环境 - 组件 1.scroll-view 组件的基本使用 2.swiper 和 swiper-item 组件的基本使用 3.text 组件的基本使用 4.rich-text 组件的基本使用 附:微信小程序轮播图单独添加图片.修改轮播图图片.单独修改某张图片 总结 小程序的宿主环境 - 组件 1.scroll-view 组件的基本使用 实现如图的纵向滚动效果 <scroll-view class="container_2" scroll-y> <vi

随机推荐