在SwiftUI中旋转视图以始终面对鼠标指针

在SwiftUI中旋转视图以始终面对鼠标指针,swift,rotation,swiftui,Swift,Rotation,Swiftui,背景:我正在尝试让视图始终旋转面对鼠标 问题详细信息:仅基于鼠标向左或向右移动或向上或向下移动视图就可以了(请参见版本1)。但是,当鼠标向下(y为负值)和左右(x为负值)以及视图位置倒退时,同时使用这两种方法(鼠标的非线性或圆弧运动)将无法工作 为此,我试图根据鼠标在屏幕的哪一侧(参见版本2),动态地将旋转值从负值切换到正值。但这感觉像是一个糟糕的实现,而且有很多问题 问题:我如何才能更好地做到这一点 我正在将光标从中取出代码 版本1- 问题:仅基于鼠标向左或向右移动或向上或向下移动来移动视图可

背景:我正在尝试让视图始终旋转面对鼠标

问题详细信息:仅基于鼠标向左或向右移动或向上或向下移动视图就可以了(请参见版本1)。但是,当鼠标向下(y为负值)和左右(x为负值)以及视图位置倒退时,同时使用这两种方法(鼠标的非线性或圆弧运动)将无法工作

为此,我试图根据鼠标在屏幕的哪一侧(参见版本2),动态地将旋转值从负值切换到正值。但这感觉像是一个糟糕的实现,而且有很多问题

问题:我如何才能更好地做到这一点

我正在将光标从中取出代码

版本1-

问题:仅基于鼠标向左或向右移动或向上或向下移动来移动视图可以正常工作,但在尝试以非线性方式移动鼠标时会出现此问题:

import SwiftUI

struct ContentView: View {
    var window: NSWindow! = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
        styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
        backing: .buffered, defer: false)
    var mouseLocation: NSPoint { NSEvent.mouseLocation }
    var location: NSPoint { window.mouseLocationOutsideOfEventStream }
    @State var position: NSPoint = NSPoint.init()
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Rectangle()
                    .fill(Color.red)
                    .frame(width: 200, height: 200)
                    .rotationEffect(
                        (self.position.y > 60) ?
                        .degrees(Double(self.position.x) / 2)
                        : .degrees(Double(self.position.x) / 2)
                    )
                    .rotationEffect(
                        (self.position.x > 320) ?
                        .degrees(Double(self.position.y * -1))
                            : .degrees(Double(self.position.y) / 2)
                    )

            }.frame(width: geometry.size.width, height: geometry.size.height)
                .onAppear() {
                    //Setup
                    self.window.center();
                    self.window.setFrameAutosaveName("Main Window")

                    /* Get mouse location on movement*/
                    NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
                        self.position = self.location //Save location
                        print("mouse location:", Double(self.position.x),  Double(self.position.y))
                        return $0
                    }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

第2版-可以正常工作,但有问题:

import SwiftUI

struct ContentView: View {
    var window: NSWindow! = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
        styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
        backing: .buffered, defer: false)
    var mouseLocation: NSPoint { NSEvent.mouseLocation }
    var location: NSPoint { window.mouseLocationOutsideOfEventStream }
    @State var position: NSPoint = NSPoint.init()
    var body: some View {
        GeometryReader { geometry in
            VStack {

                Rectangle()
                    .fill(Color.red)
                    .frame(width: 200, height: 200)
                    .rotationEffect(
                        (self.position.x > 181) ?
                        .degrees(Double(self.position.x) / 2)
                        : .degrees(Double(self.position.x * -1) / 2)
                    )
                    .rotationEffect(
                        (self.position.x > 181) ?
                        .degrees(Double(self.position.y * -1) / 2)
                        : .degrees(Double(self.position.y) / 2)
                    )

            }.frame(width: geometry.size.width, height: geometry.size.height)
                .onAppear() {
                    //Setup
                    self.window.center();
                    self.window.setFrameAutosaveName("Main Window")

                    /* Get mouse location on movement*/
                    NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
                        self.position = self.location //Save location
                        print("mouse location:", Double(self.position.x),  Double(self.position.y))
                        return $0
                    }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

看看这个:

几句话:

1) 我把它改成了ios(应该很容易改回macos,因为区别仅仅是得到了这个位置

2) 我对矩形做了一个渐变,这样可以清楚地看到矩形的旋转方向

3) 我不知道你在计算什么,但我认为你只能用三角函数来正确计算

struct ContentView: View {

    @State var position : CGPoint = .zero
    @State var offset : CGSize = .zero
    @State var translation : CGSize = .zero

    @State var angle : Double = 0

    func calcAngle(_ geometry: GeometryProxy) -> Angle {

        let rectPosition = CGPoint(x: geometry.size.width / 2, y: geometry.size.height / 2)
        // tan alpha = dx / dy
        var alpha : Double

        if rectPosition.y == position.y {
            alpha = 0
        } else {
            var dx = (position.x - rectPosition.x)
            let dy = (position.y - rectPosition.y)

            if dy > 0 {
                dx = -dx
            }

            let r = sqrt(abs(dx * dx) + abs(dy * dy))
            alpha = Double(asin(dx / r))

            if dy > 0 {
                alpha = alpha + Double.pi
            }
        }
        return Angle(radians: alpha)
    }

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Rectangle()
                    .fill(LinearGradient(gradient: Gradient(colors: [Color.red, Color.yellow]), startPoint: UnitPoint.top, endPoint: UnitPoint.bottom))
                    .frame(width: 200, height: 200)
                    .position(CGPoint(x: geometry.size.width / 2, y: geometry.size.height / 2))
                    .rotationEffect(self.calcAngle(geometry), anchor: UnitPoint(x: 0.5, y: 0.5))

                Circle().fill(Color.blue).frame(width:20,height:20).position(self.position)
                Circle().fill(Color.green).frame(width:20,height:20).position(self.position)

            }.frame(width: geometry.size.width, height: geometry.size.height)
            .gesture(
                DragGesture()
                    .onChanged { gesture in
                        self.position = gesture.location
                        self.translation = gesture.translation
                    }
                    .onEnded { _ in

                    }
            )
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

查看以下内容:

几句话:

1) 我把它改成了ios(应该很容易改回macos,因为区别仅仅是得到了这个位置

2) 我对矩形做了一个渐变,这样可以清楚地看到矩形的旋转方向

3) 我不知道你在计算什么,但我认为你只能用三角函数来正确计算

struct ContentView: View {

    @State var position : CGPoint = .zero
    @State var offset : CGSize = .zero
    @State var translation : CGSize = .zero

    @State var angle : Double = 0

    func calcAngle(_ geometry: GeometryProxy) -> Angle {

        let rectPosition = CGPoint(x: geometry.size.width / 2, y: geometry.size.height / 2)
        // tan alpha = dx / dy
        var alpha : Double

        if rectPosition.y == position.y {
            alpha = 0
        } else {
            var dx = (position.x - rectPosition.x)
            let dy = (position.y - rectPosition.y)

            if dy > 0 {
                dx = -dx
            }

            let r = sqrt(abs(dx * dx) + abs(dy * dy))
            alpha = Double(asin(dx / r))

            if dy > 0 {
                alpha = alpha + Double.pi
            }
        }
        return Angle(radians: alpha)
    }

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Rectangle()
                    .fill(LinearGradient(gradient: Gradient(colors: [Color.red, Color.yellow]), startPoint: UnitPoint.top, endPoint: UnitPoint.bottom))
                    .frame(width: 200, height: 200)
                    .position(CGPoint(x: geometry.size.width / 2, y: geometry.size.height / 2))
                    .rotationEffect(self.calcAngle(geometry), anchor: UnitPoint(x: 0.5, y: 0.5))

                Circle().fill(Color.blue).frame(width:20,height:20).position(self.position)
                Circle().fill(Color.green).frame(width:20,height:20).position(self.position)

            }.frame(width: geometry.size.width, height: geometry.size.height)
            .gesture(
                DragGesture()
                    .onChanged { gesture in
                        self.position = gesture.location
                        self.translation = gesture.translation
                    }
                    .onEnded { _ in

                    }
            )
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}