Le projet est développé sur la base du framework Wafer2. Le backend utilise le SDK Node.js fourni par Tencent Cloud pour accéder à l'API de stockage d'objets. Le code principal frontal implémente la fonction d'éditeur d'images et prend en charge le mouvement, la rotation et la mise à l'échelle. , génération d'aperçu et édition d'images et de texte. L'état est enregistré et la partie animation est implémentée à l'aide de l'animation CSS.
La zone de saisie modale de l'applet est un composant InputBox emballé séparément.
Les informations sensibles telles que AppId ont été supprimées du code. Vous pouvez ajouter votre propre AppId et AppSecret pour configurer l'environnement d'arrière-plan et implémenter les tests de connexion. Pour des méthodes d'ajout détaillées, voir « Utilisation » ci-dessous. Si vous l'exécutez localement, vous pouvez le modifier. le champ page
dans le fichier app.json
. Affichez différentes pages dans l'ordre.
git clone https://github.com/yeliudev/LemonJournal.git
LemonJournal
dans les outils de développement WeChat appid
de project.config.json
/client/utils/util.js
<image>
(correspondant à l'image) et la balise <text>
(correspondant au texte) dans le même composant personnalisé <sticker>
, d'effectuer un rendu conditionnel selon que la variable text
exposée est vide ou non. , puis liez- onTouchStart()
, onTouchEnd()
et onTouchMove()
trois événements pour gérer la position, l'angle, la taille, le niveau de l'ensemble du composant ainsi que le comportement des boutons "faire pivoter" et "supprimer". onTouchStart: function ( e ) {
// 若未选中则直接返回
if ( ! this . data . selected ) {
return
}
switch ( e . target . id ) {
case 'sticker' : {
this . touch_target = e . target . id
this . start_x = e . touches [ 0 ] . clientX * 2
this . start_y = e . touches [ 0 ] . clientY * 2
break
}
case 'handle' : {
// 隐藏移除按钮
this . setData ( {
hideRemove : true
} )
this . touch_target = e . target . id
this . start_x = e . touches [ 0 ] . clientX * 2
this . start_y = e . touches [ 0 ] . clientY * 2
this . sticker_center_x = this . data . stickerCenterX ;
this . sticker_center_y = this . data . stickerCenterY ;
this . remove_center_x = this . data . removeCenterX ;
this . remove_center_y = this . data . removeCenterY ;
this . handle_center_x = this . data . handleCenterX ;
this . handle_center_y = this . data . handleCenterY ;
this . scale = this . data . scale ;
this . rotate = this . data . rotate ;
break
}
}
} ,
onTouchEnd : function ( e ) {
this . active ( )
this . touch_target = ''
// 显示移除按钮
this . setData ( {
removeCenterX : 2 * this . data . stickerCenterX - this . data . handleCenterX ,
removeCenterY : 2 * this . data . stickerCenterY - this . data . handleCenterY ,
hideRemove : false
} )
// 若点击移除按钮则触发移除事件,否则触发刷新数据事件
if ( e . target . id === 'remove' ) {
this . triggerEvent ( 'removeSticker' , this . data . sticker_id )
} else {
this . triggerEvent ( 'refreshData' , this . data )
}
} ,
onTouchMove : function ( e ) {
// 若无选中目标则返回
if ( ! this . touch_target ) {
return
}
var current_x = e . touches [ 0 ] . clientX * 2
var current_y = e . touches [ 0 ] . clientY * 2
var diff_x = current_x - this . start_x
var diff_y = current_y - this . start_y
switch ( e . target . id ) {
case 'sticker' : {
// 拖动组件则所有控件同时移动
this . setData ( {
stickerCenterX : this . data . stickerCenterX + diff_x ,
stickerCenterY : this . data . stickerCenterY + diff_y ,
removeCenterX : this . data . removeCenterX + diff_x ,
removeCenterY : this . data . removeCenterY + diff_y ,
handleCenterX : this . data . handleCenterX + diff_x ,
handleCenterY : this . data . handleCenterY + diff_y
} )
break
}
case 'handle' : {
// 拖动操作按钮则原地旋转缩放
this . setData ( {
handleCenterX : this . data . handleCenterX + diff_x ,
handleCenterY : this . data . handleCenterY + diff_y
} )
var diff_x_before = this . handle_center_x - this . sticker_center_x ;
var diff_y_before = this . handle_center_y - this . sticker_center_y ;
var diff_x_after = this . data . handleCenterX - this . sticker_center_x ;
var diff_y_after = this . data . handleCenterY - this . sticker_center_y ;
var distance_before = Math . sqrt ( diff_x_before * diff_x_before + diff_y_before * diff_y_before ) ;
var distance_after = Math . sqrt ( diff_x_after * diff_x_after + diff_y_after * diff_y_after ) ;
var angle_before = Math . atan2 ( diff_y_before , diff_x_before ) / Math . PI * 180 ;
var angle_after = Math . atan2 ( diff_y_after , diff_x_after ) / Math . PI * 180 ;
this . setData ( {
scale : distance_after / distance_before * this . scale ,
rotate : angle_after - angle_before + this . rotate
} )
break
}
}
this . start_x = current_x ;
this . start_y = current_y ;
}
sticker
(autocollant fourni avec le logiciel), image
(image téléchargée par l'utilisateur) et text
(texte personnalisé), qui sont tous enregistrés dans un objet json au format suivant. Tous contiennent un identifiant unique et des informations associées. Lors de l'enregistrement, l'objet est généré par le client et codé dans une chaîne json et stocké dans la base de données. Lorsque l'état d'édition est restauré, l'objet est obtenu en analysant la chaîne json puis rendu. par la page d'édition. {
"backgroundId" : "5" , 背景图id
"assemblies" : [
{
"id" : "jhjg" , 组件id
"component_type" : "image" , 组件类型(自定义图片)
"image_url" : "https://example.com/jhjg.png" , 图片地址
"stickerCenterX" : 269 , 中心横坐标
"stickerCenterY" : 664 , 中心纵坐标
"scale" : 1.7123667831396403 , 缩放比例
"rotate" : - 3.0127875041833434 , 旋转角度
"wh_scale" : 1 , 图片宽高比
"z_index" : 19 组件层级
} ,
{
"id" : "gs47" ,
"component_type" : "text" , 组件类型(文字)
"text" : "test" , 文字内容
"stickerCenterX" : 479 ,
"stickerCenterY" : 546 ,
"scale" : 1.808535318980528 ,
"rotate" : 29.11614626607893 ,
"z_index" : 10
} ,
{
"id" : "chjn" ,
"component_type" : "sticker" , 组件类型(贴纸)
"sticker_type" : "food" , 贴纸类型
"sticker_id" : "1" , 贴纸id
"image_url" : "https://example.com/weapp/stickers/food/1.png" ,
"stickerCenterX" : 277 ,
"stickerCenterY" : 260 ,
"scale" : 1.3984276885130673 ,
"rotate" : - 16.620756913892055 ,
"z_index" : 5
}
]
}
Licence MIT
Copyright (c) 2021 Ye Liu