效果展示
使用方式
拖到图片上即可用
父节点会约束它的活动范围哦~
父节点会约束它的活动范围哦~
父节点会约束它的活动范围哦~
源码: 点击图片开始拖动 受到父节点区域影响
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>
/// UI DragComponent
///
/// Click on the image area to start dragging
/// The parent node scope was affected. Procedure
///
/// Easy~~~
///
/// @anchor ChenJC
/// @time: 2023/02/28
/// </summary>
public class DragComponent : MonoBehaviour, IBeginDragHandler, IDragHandler, IDropHandler
{
RectTransform rectTransform, parentRectTrans;
float minX, minY, maxX, maxY;
Vector2 offset;
public delegate void DragHandlerEvent( Vector2 currentPos );
public DragHandlerEvent dragBeginEvent;
public DragHandlerEvent dragEvent;
public DragHandlerEvent dropEvent;
#region Monobehavior Methods
private void Awake()
{
parentRectTrans = transform.parent as RectTransform;
rectTransform = transform as RectTransform;
}
private void Start()
{
var parentAnchorX = parentRectTrans.pivot.x * parentRectTrans.rect.width;
var parentAnchorY = parentRectTrans.pivot.y * parentRectTrans.rect.height;
minX = rectTransform.rect.width * 0.5f - parentAnchorX;
minY = rectTransform.rect.height * 0.5f - parentAnchorY;
maxX = parentRectTrans.rect.width - rectTransform.rect.width * 0.5f - parentAnchorX;
maxY = parentRectTrans.rect.height - rectTransform.rect.height * 0.5f - parentAnchorY;
}
#endregion
#region Internal Methods
private Vector2 ConstraintWithinParentNode( Vector2 pos )
{
pos.x = Mathf.Clamp( pos.x, minX, maxX );
pos.y = Mathf.Clamp( pos.y, minY, maxY );
return pos;
}
private bool Convert2local( Vector2 screenPos, out Vector2 localPos, Camera camera )
{
return RectTransformUtility.ScreenPointToLocalPointInRectangle( parentRectTrans, screenPos, camera, out localPos );
}
#endregion
#region Drag Handler Methods
public void OnBeginDrag( PointerEventData eventData )
{
Vector2 localPos;
if ( Convert2local( eventData.position, out localPos, eventData.pressEventCamera ) )
{
Vector2 src = rectTransform.localPosition;
offset = src - localPos;
dragBeginEvent?.Invoke( src );
}
}
public void OnDrag( PointerEventData eventData )
{
Vector2 localPos;
if ( Convert2local( eventData.position, out localPos, eventData.pressEventCamera ) )
{
Vector2 dest = ConstraintWithinParentNode( localPos + offset );
rectTransform.localPosition = dest;
dragEvent?.Invoke( dest );
}
}
public void OnDrop( PointerEventData eventData )
{
Vector2 localPos;
if ( Convert2local( eventData.position, out localPos, eventData.pressEventCamera ) )
{
Vector2 dest = ConstraintWithinParentNode( localPos + offset );
dropEvent?.Invoke( dest );
}
}
#endregion
}
拖拽事件监听
依次 开始拖拽时触发; 拖拽过程中持续触发; 拖拽结束时触发
原理介绍
开始拖拽的时候
offset = sub.localtionPos - p.localtionPos
通过计算鼠标点 计算出 相对于图片原点的 偏移 并缓存
public void OnBeginDrag( PointerEventData eventData )
{
Vector2 localPos;
if ( Convert2local( eventData.position, out localPos, eventData.pressEventCamera ) )
{
Vector2 src = rectTransform.localPosition;
offset = src - localPos;
dragBeginEvent?.Invoke( src );
}
}
拖拽过程中 我们加上这个偏移向量 就能得到相对偏移的拖拽方式
sub.locationsPos = p.locationPos + offset
public void OnDrag( PointerEventData eventData )
{
Vector2 localPos;
if ( Convert2local( eventData.position, out localPos, eventData.pressEventCamera ) )
{
Vector2 dest = ConstraintWithinParentNode( localPos + offset );
rectTransform.localPosition = dest;
dragEvent?.Invoke( dest );
}
}
限制活动范围 保持在父节点以内活动
示意图
如图可以知道 最小X 等于自身宽 的一半 同时要减去父节点 宽的一半
可以知道 最小Y 等于自身高 的一半 同时要减去父节点 高的一半
最大值 是父节点一半 - 自身大小的一半 在Unity里 你可以通过 pivot 来获取 图片锚点相对于图片自身size的百分比值 ( 0~1)
计算出最小X,最小Y,最大X,最大Y
private void Start()
{
var parentAnchorX = parentRectTrans.pivot.x * parentRectTrans.rect.width;
var parentAnchorY = parentRectTrans.pivot.y * parentRectTrans.rect.height;
minX = rectTransform.rect.width * 0.5f - parentAnchorX;
minY = rectTransform.rect.height * 0.5f - parentAnchorY;
maxX = parentRectTrans.rect.width - rectTransform.rect.width * 0.5f - parentAnchorX;
maxY = parentRectTrans.rect.height - rectTransform.rect.height * 0.5f - parentAnchorY;
}
新的位置约束在范围内
private Vector2 ConstraintWithinParentNode( Vector2 pos )
{
pos.x = Mathf.Clamp( pos.x, minX, maxX );
pos.y = Mathf.Clamp( pos.y, minY, maxY );
return pos;
}
源码: 屏幕任意位置可拖动 受到父节点区域影响
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>
/// Unrestricted UI DragComponent
///
///
/// Drag the component anywhere.
/// The parent node scope was affected. Procedure
///
/// @anchor ChenJC
/// @time: 2023/03/01
/// </summary>
[RequireComponent( typeof( RectTransform ) )]
public class UnrestrictedDragComponent : MonoBehaviour
{
public bool realtimeUpdatePosition = false, hasCaptureFingerDropPoint = false;
protected RectTransform rectTransform, parentRectTrans;
protected float minX, minY, maxX, maxY;
private Vector2 ImageRelativeDragPosition;
public delegate void DragHandlerEvent( Vector2 currentPos );
public DragHandlerEvent beginDragEvent;
public DragHandlerEvent dragEvent;
public DragHandlerEvent endDragEvent;
#region Monobehavior Methods
private void Awake()
{
parentRectTrans = transform.parent as RectTransform;
rectTransform = transform as RectTransform;
}
private void OnTransformParentChanged()
{
parentRectTrans = transform.parent as RectTransform;
}
private void OnEnable() => hasCaptureFingerDropPoint = false;
private void Update()
{
#if UNITY_EDITOR
if ( Input.anyKey )
{
if ( Input.GetMouseButtonDown( 0 ) )
{
hasCaptureFingerDropPoint = true;
TouchBegin( Input.mousePosition );
}
if ( Input.GetMouseButton( 0 ) )
{
if ( !hasCaptureFingerDropPoint )
{
hasCaptureFingerDropPoint = true;
TouchBegin( Input.mousePosition );
return;
}
TouchMoveing( Input.mousePosition );
}
}
if ( Input.GetMouseButtonUp( 0 ) )
{
TouchEnd( Input.mousePosition );
}
#elif UNITY_ANDROID || UNITY_IOS
if ( Input.touchSupported && Input.touchCount > 0 )
{
var touch = Input.GetTouch( 0 );
if ( touch.phase == TouchPhase.Began )
{
hasCaptureFingerDropPoint = true;
TouchBegin( touch.position );
}
if ( touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary )
{
if ( !hasCaptureFingerDropPoint )
{
hasCaptureFingerDropPoint = true;
TouchBegin( Input.mousePosition );
return;
}
TouchMoveing( touch.position );
}
if ( touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled )
{
TouchEnd( touch.position );
}
}
#endif
if ( realtimeUpdatePosition )
{
rectTransform.localPosition = ConstraintWithinParentNode( rectTransform.localPosition );
}
}
#endregion
#region Internal Methods
//实时计算包围盒
protected virtual void RealtimeCalcBounds()
{
var rad = rectTransform.localEulerAngles.z * Mathf.Deg2Rad;
var a = rectTransform.rect.height * ( 1.0f - rectTransform.pivot.y ) * Mathf.Cos( rad );
var b = rectTransform.rect.height * rectTransform.pivot.y * Mathf.Sin( rad );
var selfMinX = Mathf.Abs( Mathf.Min( a, b ) );
var selfMaxX = Mathf.Abs( Mathf.Max( a, b ) );
var c = rectTransform.rect.height * rectTransform.pivot.y * Mathf.Cos( rad );
var d = rectTransform.rect.height * ( 1.0f - rectTransform.pivot.y ) * Mathf.Sin( rad );
var selfMinY = Mathf.Abs( Mathf.Max( c, d ) );
var selfMaxY = Mathf.Abs( Mathf.Min( c, d ) );
var layoutOffsetX = ( rectTransform.pivot.x - parentRectTrans.anchorMin.x ) * rectTransform.rect.width;
var layoutOffsetY = ( rectTransform.pivot.y - parentRectTrans.anchorMin.y ) * rectTransform.rect.height;
minX = selfMinX + layoutOffsetX - parentRectTrans.pivot.x * parentRectTrans.rect.width;
minY = selfMinY + layoutOffsetY - parentRectTrans.pivot.y * parentRectTrans.rect.height;
maxX = parentRectTrans.rect.width * ( 1.0f - parentRectTrans.pivot.x ) - selfMaxX;
maxY = parentRectTrans.rect.height * ( 1.0f - parentRectTrans.pivot.y ) - selfMaxY;
}
//约束在父节点以内
private Vector2 ConstraintWithinParentNode( Vector2 pos )
{
RealtimeCalcBounds();
pos.x = Mathf.Clamp( pos.x, minX, maxX );
pos.y = Mathf.Clamp( pos.y, minY, maxY );
return pos;
}
//如果你的相机 不是用 MainCamera 来渲染UI
//请将这里的Camera调整成对应Canvas Camera
private bool Convert2local( Vector2 screenPos, out Vector2 localPos )
{
return RectTransformUtility.ScreenPointToLocalPointInRectangle( parentRectTrans, screenPos, Camera.main, out localPos );
}
/// <summary>
/// 更新位置
/// </summary>
private void UpdatePosition( ref Vector2 pos )
{
if ( Convert2local( pos, out Vector2 localpos ) )
{
rectTransform.localPosition = ConstraintWithinParentNode( localpos + ImageRelativeDragPosition );
}
}
#endregion
#region Touch Handler Methods
//开始点击的时候触发
private void TouchBegin( Vector2 pos )
{
if ( Convert2local( pos, out Vector2 localpos ) )
{
Vector2 src = rectTransform.localPosition;
ImageRelativeDragPosition = src - localpos;
beginDragEvent?.Invoke( src );
}
}
//拖拽的时候触发
private void TouchMoveing( Vector2 pos )
{
UpdatePosition( ref pos );
dragEvent?.Invoke( rectTransform.localPosition );
}
//抬起手指的时候触发
private void TouchEnd( Vector2 pos )
{
UpdatePosition( ref pos );
endDragEvent?.Invoke( rectTransform.localPosition );
}
#endregion
}