ThirdView.ets 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import { HeroDeedItem } from '../model/Index'
  2. import { ThirdViewModel } from '../viewModel/ThirdViewModel'
  3. import { Location } from '../model/AnimationModel'
  4. import { IBestToast } from 'basic'
  5. import { HistoryApis } from '../Apis/HistoryApis'
  6. @ComponentV2
  7. export struct ThirdView {
  8. vm: ThirdViewModel = new ThirdViewModel()
  9. build() {
  10. Column() {
  11. Row(){
  12. Text('英雄人物模范事迹')
  13. .fontSize(18)
  14. .fontWeight(500)
  15. .fontColor('#FF333333')
  16. }
  17. .width("100%")
  18. .padding({top: 28, bottom: 15})
  19. .justifyContent(FlexAlign.Center)
  20. List({space: 10}){
  21. Repeat(this.vm.heroicModelCategory)
  22. .each((item) => {
  23. ListItem(){
  24. Text(item.item)
  25. .fontSize(14)
  26. .borderRadius(44)
  27. .padding({left: 16, top: 6, right: 16, bottom: 6})
  28. .fontWeight(this.vm.currentCategory == item.index ? 700 : 400)
  29. .fontColor(this.vm.currentCategory == item.index ? '#FFFFFFFF' : '#FF787878')
  30. .backgroundColor(this.vm.currentCategory == item.index ? '#FF45E595' : '#FFFFFFFF')
  31. .shadow(this.vm.currentCategory == item.index ? null : { color: '#1A000000', radius: 4 })
  32. }
  33. .onClick(() => { this.vm.changeCategory(item.index) })
  34. })
  35. .key((item) => item)
  36. }
  37. .height(32)
  38. .width("100%")
  39. .scrollBar(BarState.Off)
  40. .listDirection(Axis.Horizontal)
  41. Column(){
  42. List({space: 20}){
  43. Repeat(this.vm.dataSource)
  44. .each((item) => {
  45. ListItem(){
  46. HeroItem({data: item.item, flowerEvent: (i: HeroDeedItem) =>{ this.vm.onFlower(i, item.index) }})
  47. }
  48. })
  49. .key((item) => JSON.stringify(item))
  50. }
  51. .width("100%")
  52. .height('100%')
  53. .scrollBar(BarState.Off)
  54. .onReachEnd(() => { if(this.vm.dataSource.length != 0) this.vm.getHeroDeedList(false) })
  55. }
  56. .width("100%")
  57. .layoutWeight(1)
  58. }
  59. .width('100%')
  60. .height('100%')
  61. .padding({left: 17, right: 17, top: this.vm.safeTop})
  62. .linearGradient({colors: [['#FFF4FFF1', 0], ['#FFFFFFFF', 0.8]]})
  63. }
  64. }
  65. // 英雄人物事迹 Item
  66. @ComponentV2
  67. struct HeroItem{
  68. @Param @Require data: HeroDeedItem
  69. @Event flowerEvent: (data: HeroDeedItem) => void
  70. // 详情是否展开 - 默认收起
  71. @Local isOpen: boolean = false
  72. @Local height_: number = 207
  73. // 开始撒花动画
  74. @Local flowerAnimation: boolean = false
  75. // 花瓣的坐标数组
  76. @Local flowerCoordinates: Array<Location> = []
  77. // 打开、关闭 详情
  78. changeDetail(){
  79. this.getUIContext().animateTo({ duration: 300 }, () =>{
  80. this.isOpen = !this.isOpen
  81. })
  82. }
  83. // 献花
  84. async onFlower(){
  85. try {
  86. let ans = await HistoryApis.giveFlowers(this.data.contentId!)
  87. console.log(`ans = ${JSON.stringify(ans)}`)
  88. this.flowerEvent(ans)
  89. this.startFlowerAnimation()
  90. } catch (e) {
  91. // IBestToast.show('献花失败')
  92. }
  93. }
  94. // 开启献花动画
  95. startFlowerAnimation(){
  96. IBestToast.show(`感谢您为${this.data?.heroName}英雄献花`)
  97. this.generateFlowerCoordinates(true)
  98. setTimeout(() => {
  99. this.generateFlowerCoordinates(false)
  100. }, 100)
  101. }
  102. // 生成花瓣坐标
  103. generateFlowerCoordinates(isStart: boolean = true){
  104. if(isStart){
  105. this.flowerCoordinates = []
  106. let len = this.isOpen ? 10 : 7
  107. for(let i = 0; i < len; i++){
  108. let x = 10 + Math.random() * 70
  109. let y = 10 + Math.random() * (this.height_ - 40)
  110. let location: Location = new Location(x, y)
  111. this.flowerCoordinates.push(location)
  112. }
  113. this.flowerAnimation = true
  114. } else {
  115. for (let i = 0; i < this.flowerCoordinates.length; i++) {
  116. let f = new Location(this.flowerCoordinates[i].x, this.flowerCoordinates[i].y)
  117. // 将原来的固定值修改为随机值
  118. // f.x += (Math.random() > 0.5 ? 1 : -1) * (5 + Math.random() * 15)
  119. f.y += 15 + Math.random() * (this.isOpen ? 300 : 50)
  120. this.getUIContext().animateTo({ duration: 800 }, () => {
  121. this.flowerCoordinates.splice(i, 1, f)
  122. })
  123. }
  124. setTimeout(() => {
  125. this.flowerAnimation = false
  126. }, 800)
  127. }
  128. }
  129. build() {
  130. Stack(){
  131. Column(){
  132. Column(){
  133. Row(){
  134. Text(this.data?.heroName)
  135. .fontSize(22)
  136. .fontWeight(800)
  137. .fontColor('#FF333333')
  138. Text(this.data?.period)
  139. .fontSize(14)
  140. .fontWeight(400)
  141. .fontColor('#FF333333')
  142. }
  143. .width("100%")
  144. .justifyContent(FlexAlign.SpaceBetween)
  145. Row(){
  146. Text(this.data?.description)
  147. .fontSize(14)
  148. .width("100%")
  149. .fontWeight(400)
  150. .fontColor('#FF333333')
  151. }
  152. .width("100%")
  153. .padding({top: 29})
  154. Row(){
  155. Row({space: 8}){
  156. Image($r('app.media.icon_flower'))
  157. .width(38)
  158. .aspectRatio(1)
  159. Text(){
  160. Span(this.data?.flowersNum ?? '0')
  161. .fontSize(20)
  162. .fontWeight(400)
  163. .fontColor('#FFFF7B00')
  164. Span('朵鲜花')
  165. .fontSize(12)
  166. .fontWeight(400)
  167. .fontColor('#FF333333')
  168. }
  169. }
  170. Row(){
  171. Text('献花')
  172. .fontSize(14)
  173. .fontWeight(400)
  174. .fontColor('#FFFFFFFF')
  175. }
  176. .width(85)
  177. .height(30)
  178. .borderRadius(8)
  179. .alignItems(VerticalAlign.Center)
  180. .justifyContent(FlexAlign.Center)
  181. .linearGradient({colors: [['#FFFFCA00', 0], ['#FFFF9D01', 0.5]], angle: 90})
  182. .onClick(() => { this.onFlower() })
  183. }
  184. .width("100%")
  185. .padding({top: 13, bottom: 30})
  186. .justifyContent(FlexAlign.SpaceBetween)
  187. }
  188. .background(this.bgc(), {align: Alignment.Top})
  189. .padding({left: 13, right: 13, top: 5})
  190. Column(){
  191. Column({space: 6}){
  192. this.fontComp('英雄事迹')
  193. Text(this.data.heroicDeeds)
  194. .width("100%")
  195. .fontSize(12)
  196. .fontWeight(400)
  197. .lineHeight(20)
  198. this.fontComp('精神内涵')
  199. Row({space: 12}){
  200. ForEach(this.data.type?.split('、'), (item: string, index) => {
  201. Text(item)
  202. .fontSize(12)
  203. .fontWeight(400)
  204. .borderRadius(5)
  205. .fontColor('#52886F')
  206. .backgroundColor('#F2F2F2')
  207. .padding({left: 13, top: 6, right: 13, bottom: 6})
  208. })
  209. }
  210. .width("100%")
  211. .justifyContent(FlexAlign.Start)
  212. .padding({bottom: 37})
  213. }
  214. .height(this.isOpen ? '' : 0)
  215. // .backgroundColor(Color.Black)
  216. Row({space: 7}){
  217. Text(this.isOpen ? '收起详情' : '展开详情')
  218. .fontSize(10)
  219. .fontWeight(400)
  220. .fontColor('#FF52886F')
  221. Image($r('app.media.icon_heroOpen'))
  222. .width(13)
  223. .aspectRatio(1)
  224. .rotate({angle: this.isOpen ? 180 : 0})
  225. }
  226. .width("100%")
  227. .padding({top: 6, bottom: 6})
  228. .justifyContent(FlexAlign.Center)
  229. .onClick(() => { this.changeDetail() })
  230. }
  231. .padding({left: 13, right: 13})
  232. }
  233. .width("100%")
  234. .justifyContent(FlexAlign.Start)
  235. .alignItems(HorizontalAlign.Center)
  236. .onAreaChange((o, n) => {
  237. if(this.isOpen){
  238. this.height_ = Math.max(this.height_, n.height as number)
  239. } else {
  240. this.height_ = Math.min(this.height_, n.height as number)
  241. }
  242. console.log(`this.height_ = ${JSON.stringify(this.height_)}`)
  243. })
  244. if(this.flowerAnimation) {
  245. Column(){
  246. Repeat(this.flowerCoordinates)
  247. .each((item) => {
  248. Image($r('app.media.icon_hero_flower'))
  249. .width(36)
  250. .aspectRatio(1)
  251. .zIndex(999)
  252. .position({ x: `${item.item.x}%`, y: item.item.y })
  253. .opacity(this.flowerAnimation ? 1 : 0)
  254. })
  255. .key((item) => JSON.stringify(item))
  256. }
  257. .width("100%")
  258. .height(this.height_)
  259. .opacity(this.flowerAnimation ? 1 : 0)
  260. }
  261. }
  262. .width('100%')
  263. .borderRadius(8)
  264. .padding({bottom: 8})
  265. .backgroundColor(Color.White)
  266. .shadow({ color: '#1A000000', radius: 4 })
  267. }
  268. @Builder
  269. bgc(){
  270. Image($r('app.media.img_heroBgImg'))
  271. .width("100%")
  272. .height(207)
  273. }
  274. @Builder
  275. fontComp(text: string){
  276. Stack({alignContent: Alignment.BottomStart}){
  277. Image($r('app.media.icon_unLine'))
  278. .width(56)
  279. .height(13)
  280. .visibility(this.isOpen ? Visibility.Visible : Visibility.Hidden)
  281. Row(){
  282. Text(text)
  283. .fontSize(14)
  284. .fontWeight(500)
  285. .fontColor('#333333')
  286. }
  287. .width("100%")
  288. .padding({bottom: 5})
  289. .justifyContent(FlexAlign.Start)
  290. }
  291. }
  292. }