Я пытаюсь реализовать эффект перспективы варп на текст в Swiftui. Однако, когда я пытаюсь преобразовать текстовый путь, он только перемещает текст, а не растягивая или сжимая его, как и ожидалось в перспективной трансформации. Моя цель состоит в том, чтобы исказить текст вдоль набора точек (левый верхний, верхний, правый, правый нижний и нижний левый), чтобы создать перспективный эффект, аналогичный редактору фото. < /P>
Вот код, который я использую для достижения эффекта:
import SwiftUI
struct PerspectiveWarpView: View {
@State private var points: [CGPoint] = [] // Initially empty
@State var color: Color = .white
@State var position: CGPoint = CGPoint(x: 100, y: 300)
@State var position2: CGPoint = CGPoint(x: 100, y: 250)
@State private var initialPosition: CGPoint = .zero
var body: some View {
GeometryReader { geometry in
ZStack {
Color.black.edgesIgnoringSafeArea(.all)
if !points.isEmpty, let warpedPath = transformTextPath() {
warpedPath
.fill(color)
.position(position)
.gesture(DragGesture().onChanged { value in
if initialPosition == .zero {
initialPosition = position
}
let newPosition = CGPoint(
x: initialPosition.x + value.translation.width,
y: initialPosition.y + value.translation.height
)
DispatchQueue.main.async {
position = newPosition
position2 = CGPoint(x: newPosition.x, y: newPosition.y - 50)
}
}.onEnded({ _ in
initialPosition = .zero
})
)
PointsView(points: $points, path: warpedPath)
.position(position2)
.onAppear(){
points = getCorners(of: warpedPath)
}
}
}
.onAppear {
// Initialize points based on the screen size
let screenWidth = geometry.size.width
let screenHeight = geometry.size.height
let offsetX = (screenWidth - 300) / 2 // Center horizontally
let offsetY = (screenHeight - 200) / 2 // Center vertically
points = [
CGPoint(x: offsetX + 0, y: offsetY + 0), // Top-left
CGPoint(x: offsetX + 300, y: offsetY + 0), // Top-right
CGPoint(x: offsetX + 300, y: offsetY + 200), // Bottom-right
CGPoint(x: offsetX + 0, y: offsetY + 200) // Bottom-left
]
}
}
}
func getCorners(of path: Path) -> [CGPoint] {
let boundingBox = path.boundingRect
return [
CGPoint(x: boundingBox.minX, y: boundingBox.minY - 10), // Top-left
CGPoint(x: boundingBox.maxX, y: boundingBox.minY - 10), // Top-right
CGPoint(x: boundingBox.maxX, y: boundingBox.maxY + 10), // Bottom-right
CGPoint(x: boundingBox.minX, y: boundingBox.maxY + 10) // Bottom-left
]
}
func transformTextPath() -> Path? {
guard !points.isEmpty else { return nil } // Ensure points are not empty
guard let originalPath = textToPath(text: "ELEVATED", font: .systemFont(ofSize: 80, weight: .bold)) else {
return nil
}
// Apply perspective transform to the path
return warpPath(originalPath, from: defaultRect(), to: points)
}
func textToPath(text: String, font: UIFont) -> Path? {
let attributedString = NSAttributedString(string: text, attributes: [.font: font])
let line = CTLineCreateWithAttributedString(attributedString)
let runArray = CTLineGetGlyphRuns(line) as NSArray
let path = CGMutablePath()
for run in runArray {
let run = run as! CTRun
let count = CTRunGetGlyphCount(run)
for index in 0.. [CGPoint] {
return [
CGPoint(x: 0, y: 0), // Top-left
CGPoint(x: 300, y: 0), // Top-right
CGPoint(x: 300, y: 200), // Bottom-right
CGPoint(x: 0, y: 200) // Bottom-left
]
}
func warpPath(_ path: Path, from src: [CGPoint], to dst: [CGPoint]) -> Path {
var newPath = Path()
let transform = computePerspectiveTransform(from: src, to: dst)
path.forEach { element in
switch element {
case .move(to: let point):
newPath.move(to: applyPerspective(point, using: transform))
case .line(to: let point):
newPath.addLine(to: applyPerspective(point, using: transform))
case .quadCurve(to: let point, control: let control):
newPath.addQuadCurve(to: applyPerspective(point, using: transform),
control: applyPerspective(control, using: transform))
case .curve(to: let point, control1: let control1, control2: let control2):
newPath.addCurve(to: applyPerspective(point, using: transform),
control1: applyPerspective(control1, using: transform),
control2: applyPerspective(control2, using: transform))
case .closeSubpath:
newPath.closeSubpath()
}
}
return newPath
}
func computePerspectiveTransform(from src: [CGPoint], to dst: [CGPoint]) -> [[CGFloat]] {
let x0 = src[0].x, y0 = src[0].y
let x1 = src[1].x, y1 = src[1].y
let x2 = src[2].x, y2 = src[2].y
let x3 = src[3].x, y3 = src[3].y
let X0 = dst[0].x, Y0 = dst[0].y
let X1 = dst[1].x, Y1 = dst[1].y
let X2 = dst[2].x, Y2 = dst[2].y
let X3 = dst[3].x, Y3 = dst[3].y
let A = [
[x0, y0, 1, 0, 0, 0, -X0*x0, -X0*y0],
[x1, y1, 1, 0, 0, 0, -X1*x1, -X1*y1],
[x2, y2, 1, 0, 0, 0, -X2*x2, -X2*y2],
[x3, y3, 1, 0, 0, 0, -X3*x3, -X3*y3],
[0, 0, 0, x0, y0, 1, -Y0*x0, -Y0*y0],
[0, 0, 0, x1, y1, 1, -Y1*x1, -Y1*y1],
[0, 0, 0, x2, y2, 1, -Y2*x2, -Y2*y2],
[0, 0, 0, x3, y3, 1, -Y3*x3, -Y3*y3]
]
let B = [X0, X1, X2, X3, Y0, Y1, Y2, Y3]
guard let h = solveLinearSystem(A: A, B: B) else {
return [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
}
return [
[h[0], h[1], h[2]],
[h[3], h[4], h[5]],
[h[6], h[7], 1]
]
}
func solveLinearSystem(A: [[CGFloat]], B: [CGFloat]) -> [CGFloat]? {
let rowCount = A.count
var matrix = A
var result = B
for i in 0..
Подробнее здесь: https://stackoverflow.com/questions/794 ... t-moves-in
Парку, чтобы применить перспективную варку к текстовому пути в Swiftui, текст просто движется вместо растяжения или сжим ⇐ IOS
Программируем под IOS
1738327837
Anonymous
Я пытаюсь реализовать эффект перспективы варп на текст в Swiftui. Однако, когда я пытаюсь преобразовать текстовый путь, он только перемещает текст, а не растягивая или сжимая его, как и ожидалось в перспективной трансформации. Моя цель состоит в том, чтобы исказить текст вдоль набора точек (левый верхний, верхний, правый, правый нижний и нижний левый), чтобы создать перспективный эффект, аналогичный редактору фото. < /P>
[b] Вот код, который я использую для достижения эффекта: [/b]
import SwiftUI
struct PerspectiveWarpView: View {
@State private var points: [CGPoint] = [] // Initially empty
@State var color: Color = .white
@State var position: CGPoint = CGPoint(x: 100, y: 300)
@State var position2: CGPoint = CGPoint(x: 100, y: 250)
@State private var initialPosition: CGPoint = .zero
var body: some View {
GeometryReader { geometry in
ZStack {
Color.black.edgesIgnoringSafeArea(.all)
if !points.isEmpty, let warpedPath = transformTextPath() {
warpedPath
.fill(color)
.position(position)
.gesture(DragGesture().onChanged { value in
if initialPosition == .zero {
initialPosition = position
}
let newPosition = CGPoint(
x: initialPosition.x + value.translation.width,
y: initialPosition.y + value.translation.height
)
DispatchQueue.main.async {
position = newPosition
position2 = CGPoint(x: newPosition.x, y: newPosition.y - 50)
}
}.onEnded({ _ in
initialPosition = .zero
})
)
PointsView(points: $points, path: warpedPath)
.position(position2)
.onAppear(){
points = getCorners(of: warpedPath)
}
}
}
.onAppear {
// Initialize points based on the screen size
let screenWidth = geometry.size.width
let screenHeight = geometry.size.height
let offsetX = (screenWidth - 300) / 2 // Center horizontally
let offsetY = (screenHeight - 200) / 2 // Center vertically
points = [
CGPoint(x: offsetX + 0, y: offsetY + 0), // Top-left
CGPoint(x: offsetX + 300, y: offsetY + 0), // Top-right
CGPoint(x: offsetX + 300, y: offsetY + 200), // Bottom-right
CGPoint(x: offsetX + 0, y: offsetY + 200) // Bottom-left
]
}
}
}
func getCorners(of path: Path) -> [CGPoint] {
let boundingBox = path.boundingRect
return [
CGPoint(x: boundingBox.minX, y: boundingBox.minY - 10), // Top-left
CGPoint(x: boundingBox.maxX, y: boundingBox.minY - 10), // Top-right
CGPoint(x: boundingBox.maxX, y: boundingBox.maxY + 10), // Bottom-right
CGPoint(x: boundingBox.minX, y: boundingBox.maxY + 10) // Bottom-left
]
}
func transformTextPath() -> Path? {
guard !points.isEmpty else { return nil } // Ensure points are not empty
guard let originalPath = textToPath(text: "ELEVATED", font: .systemFont(ofSize: 80, weight: .bold)) else {
return nil
}
// Apply perspective transform to the path
return warpPath(originalPath, from: defaultRect(), to: points)
}
func textToPath(text: String, font: UIFont) -> Path? {
let attributedString = NSAttributedString(string: text, attributes: [.font: font])
let line = CTLineCreateWithAttributedString(attributedString)
let runArray = CTLineGetGlyphRuns(line) as NSArray
let path = CGMutablePath()
for run in runArray {
let run = run as! CTRun
let count = CTRunGetGlyphCount(run)
for index in 0.. [CGPoint] {
return [
CGPoint(x: 0, y: 0), // Top-left
CGPoint(x: 300, y: 0), // Top-right
CGPoint(x: 300, y: 200), // Bottom-right
CGPoint(x: 0, y: 200) // Bottom-left
]
}
func warpPath(_ path: Path, from src: [CGPoint], to dst: [CGPoint]) -> Path {
var newPath = Path()
let transform = computePerspectiveTransform(from: src, to: dst)
path.forEach { element in
switch element {
case .move(to: let point):
newPath.move(to: applyPerspective(point, using: transform))
case .line(to: let point):
newPath.addLine(to: applyPerspective(point, using: transform))
case .quadCurve(to: let point, control: let control):
newPath.addQuadCurve(to: applyPerspective(point, using: transform),
control: applyPerspective(control, using: transform))
case .curve(to: let point, control1: let control1, control2: let control2):
newPath.addCurve(to: applyPerspective(point, using: transform),
control1: applyPerspective(control1, using: transform),
control2: applyPerspective(control2, using: transform))
case .closeSubpath:
newPath.closeSubpath()
}
}
return newPath
}
func computePerspectiveTransform(from src: [CGPoint], to dst: [CGPoint]) -> [[CGFloat]] {
let x0 = src[0].x, y0 = src[0].y
let x1 = src[1].x, y1 = src[1].y
let x2 = src[2].x, y2 = src[2].y
let x3 = src[3].x, y3 = src[3].y
let X0 = dst[0].x, Y0 = dst[0].y
let X1 = dst[1].x, Y1 = dst[1].y
let X2 = dst[2].x, Y2 = dst[2].y
let X3 = dst[3].x, Y3 = dst[3].y
let A = [
[x0, y0, 1, 0, 0, 0, -X0*x0, -X0*y0],
[x1, y1, 1, 0, 0, 0, -X1*x1, -X1*y1],
[x2, y2, 1, 0, 0, 0, -X2*x2, -X2*y2],
[x3, y3, 1, 0, 0, 0, -X3*x3, -X3*y3],
[0, 0, 0, x0, y0, 1, -Y0*x0, -Y0*y0],
[0, 0, 0, x1, y1, 1, -Y1*x1, -Y1*y1],
[0, 0, 0, x2, y2, 1, -Y2*x2, -Y2*y2],
[0, 0, 0, x3, y3, 1, -Y3*x3, -Y3*y3]
]
let B = [X0, X1, X2, X3, Y0, Y1, Y2, Y3]
guard let h = solveLinearSystem(A: A, B: B) else {
return [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
}
return [
[h[0], h[1], h[2]],
[h[3], h[4], h[5]],
[h[6], h[7], 1]
]
}
func solveLinearSystem(A: [[CGFloat]], B: [CGFloat]) -> [CGFloat]? {
let rowCount = A.count
var matrix = A
var result = B
for i in 0..
Подробнее здесь: [url]https://stackoverflow.com/questions/79402781/struggling-to-apply-perspective-warp-to-text-path-in-swiftui-text-just-moves-in[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия