欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-22 19:09:19
本文实例为大家分享了ugui轮播图组件实现的具体代码,供大家参考,具体内容如下 要用到,于是就自已做了一个,自认为封装上还是ok的,开发于unity5.1.2。 支持自...

本文实例为大家分享了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轮播图组件实现方法详解

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

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