├── .gitignore ├── assets ├── Scene.meta ├── Scene │ ├── main.fire │ └── main.fire.meta ├── Script.meta ├── Script │ ├── LabelEvent.ts │ ├── LabelEvent.ts.meta │ ├── RichText.ts │ ├── RichText.ts.meta │ ├── html-text-parser.js │ └── html-text-parser.js.meta ├── Texture.meta └── Texture │ ├── faces.plist │ ├── faces.plist.meta │ ├── faces.png │ └── faces.png.meta ├── creator.d.ts ├── doc ├── preview.png └── preview2.jpg ├── jsconfig.json ├── project.json ├── readme.md └── settings ├── project.json └── services.json /.gitignore: -------------------------------------------------------------------------------- 1 | #///////////////////////////////////////////////////////////////////////////// 2 | # Fireball Projects 3 | #///////////////////////////////////////////////////////////////////////////// 4 | 5 | /library/ 6 | /temp/ 7 | /local/ 8 | /build/ 9 | 10 | #///////////////////////////////////////////////////////////////////////////// 11 | # npm files 12 | #///////////////////////////////////////////////////////////////////////////// 13 | 14 | npm-debug.log 15 | node_modules/ 16 | 17 | #///////////////////////////////////////////////////////////////////////////// 18 | # Logs and databases 19 | #///////////////////////////////////////////////////////////////////////////// 20 | 21 | *.log 22 | *.sql 23 | *.sqlite 24 | 25 | #///////////////////////////////////////////////////////////////////////////// 26 | # files for debugger 27 | #///////////////////////////////////////////////////////////////////////////// 28 | 29 | *.sln 30 | *.csproj 31 | *.pidb 32 | *.unityproj 33 | *.suo 34 | 35 | #///////////////////////////////////////////////////////////////////////////// 36 | # OS generated files 37 | #///////////////////////////////////////////////////////////////////////////// 38 | 39 | .DS_Store 40 | ehthumbs.db 41 | Thumbs.db 42 | 43 | #///////////////////////////////////////////////////////////////////////////// 44 | # WebStorm files 45 | #///////////////////////////////////////////////////////////////////////////// 46 | 47 | .idea/ 48 | 49 | #////////////////////////// 50 | # VS Code files 51 | #////////////////////////// 52 | 53 | .vscode/ 54 | -------------------------------------------------------------------------------- /assets/Scene.meta: -------------------------------------------------------------------------------- 1 | { 2 | "ver": "1.0.1", 3 | "uuid": "b7e0f23d-eccb-4016-80a7-e749de64643f", 4 | "isSubpackage": false, 5 | "subpackageName": "", 6 | "subMetas": {} 7 | } -------------------------------------------------------------------------------- /assets/Scene/main.fire: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "__type__": "cc.SceneAsset", 4 | "_name": "", 5 | "_objFlags": 0, 6 | "_native": "", 7 | "scene": { 8 | "__id__": 1 9 | } 10 | }, 11 | { 12 | "__type__": "cc.Scene", 13 | "_objFlags": 0, 14 | "_parent": null, 15 | "_children": [ 16 | { 17 | "__id__": 2 18 | } 19 | ], 20 | "_active": true, 21 | "_level": 0, 22 | "_components": [], 23 | "_prefab": null, 24 | "_opacity": 255, 25 | "_color": { 26 | "__type__": "cc.Color", 27 | "r": 255, 28 | "g": 255, 29 | "b": 255, 30 | "a": 255 31 | }, 32 | "_contentSize": { 33 | "__type__": "cc.Size", 34 | "width": 0, 35 | "height": 0 36 | }, 37 | "_anchorPoint": { 38 | "__type__": "cc.Vec2", 39 | "x": 0, 40 | "y": 0 41 | }, 42 | "_scale": { 43 | "__type__": "cc.Vec3", 44 | "x": 1, 45 | "y": 1, 46 | "z": 1 47 | }, 48 | "_quat": { 49 | "__type__": "cc.Quat", 50 | "x": 0, 51 | "y": 0, 52 | "z": 0, 53 | "w": 1 54 | }, 55 | "groupIndex": 0, 56 | "autoReleaseAssets": false, 57 | "_id": "a10c3d2c-2d1f-4036-b01b-1af96cc46adf" 58 | }, 59 | { 60 | "__type__": "cc.Node", 61 | "_name": "Canvas", 62 | "_objFlags": 0, 63 | "_parent": { 64 | "__id__": 1 65 | }, 66 | "_children": [ 67 | { 68 | "__id__": 3 69 | }, 70 | { 71 | "__id__": 5 72 | } 73 | ], 74 | "_active": true, 75 | "_level": 0, 76 | "_components": [ 77 | { 78 | "__id__": 8 79 | } 80 | ], 81 | "_prefab": null, 82 | "_opacity": 255, 83 | "_color": { 84 | "__type__": "cc.Color", 85 | "r": 255, 86 | "g": 255, 87 | "b": 255, 88 | "a": 255 89 | }, 90 | "_contentSize": { 91 | "__type__": "cc.Size", 92 | "width": 960, 93 | "height": 640 94 | }, 95 | "_anchorPoint": { 96 | "__type__": "cc.Vec2", 97 | "x": 0.5, 98 | "y": 0.5 99 | }, 100 | "_position": { 101 | "__type__": "cc.Vec3", 102 | "x": 480, 103 | "y": 320, 104 | "z": 0 105 | }, 106 | "_scale": { 107 | "__type__": "cc.Vec3", 108 | "x": 1, 109 | "y": 1, 110 | "z": 1 111 | }, 112 | "_rotationX": 0, 113 | "_rotationY": 0, 114 | "_quat": { 115 | "__type__": "cc.Quat", 116 | "x": 0, 117 | "y": 0, 118 | "z": 0, 119 | "w": 1 120 | }, 121 | "_skewX": 0, 122 | "_skewY": 0, 123 | "groupIndex": 0, 124 | "_id": "e6nPSfpgBKK7rR8yeJLvPL" 125 | }, 126 | { 127 | "__type__": "cc.Node", 128 | "_name": "Main Camera", 129 | "_objFlags": 0, 130 | "_parent": { 131 | "__id__": 2 132 | }, 133 | "_children": [], 134 | "_active": true, 135 | "_level": 1, 136 | "_components": [ 137 | { 138 | "__id__": 4 139 | } 140 | ], 141 | "_prefab": null, 142 | "_opacity": 255, 143 | "_color": { 144 | "__type__": "cc.Color", 145 | "r": 255, 146 | "g": 255, 147 | "b": 255, 148 | "a": 255 149 | }, 150 | "_contentSize": { 151 | "__type__": "cc.Size", 152 | "width": 0, 153 | "height": 0 154 | }, 155 | "_anchorPoint": { 156 | "__type__": "cc.Vec2", 157 | "x": 0.5, 158 | "y": 0.5 159 | }, 160 | "_position": { 161 | "__type__": "cc.Vec3", 162 | "x": 0, 163 | "y": 0, 164 | "z": 0 165 | }, 166 | "_scale": { 167 | "__type__": "cc.Vec3", 168 | "x": 1, 169 | "y": 1, 170 | "z": 1 171 | }, 172 | "_rotationX": 0, 173 | "_rotationY": 0, 174 | "_quat": { 175 | "__type__": "cc.Quat", 176 | "x": 0, 177 | "y": 0, 178 | "z": 0, 179 | "w": 1 180 | }, 181 | "_skewX": 0, 182 | "_skewY": 0, 183 | "groupIndex": 0, 184 | "_id": "dfpJmZ1/5DBaddgwHsF85q" 185 | }, 186 | { 187 | "__type__": "cc.Camera", 188 | "_name": "", 189 | "_objFlags": 0, 190 | "node": { 191 | "__id__": 3 192 | }, 193 | "_enabled": true, 194 | "_cullingMask": 4294967295, 195 | "_clearFlags": 7, 196 | "_backgroundColor": { 197 | "__type__": "cc.Color", 198 | "r": 0, 199 | "g": 0, 200 | "b": 0, 201 | "a": 255 202 | }, 203 | "_depth": -1, 204 | "_zoomRatio": 1, 205 | "_targetTexture": null, 206 | "_id": "a4+LLSGDhChIBUsl+Oa3Vo" 207 | }, 208 | { 209 | "__type__": "cc.Node", 210 | "_name": "RichText", 211 | "_objFlags": 0, 212 | "_parent": { 213 | "__id__": 2 214 | }, 215 | "_children": [], 216 | "_active": true, 217 | "_level": 1, 218 | "_components": [ 219 | { 220 | "__id__": 6 221 | }, 222 | { 223 | "__id__": 7 224 | } 225 | ], 226 | "_prefab": null, 227 | "_opacity": 255, 228 | "_color": { 229 | "__type__": "cc.Color", 230 | "r": 255, 231 | "g": 255, 232 | "b": 255, 233 | "a": 255 234 | }, 235 | "_contentSize": { 236 | "__type__": "cc.Size", 237 | "width": 0, 238 | "height": 0 239 | }, 240 | "_anchorPoint": { 241 | "__type__": "cc.Vec2", 242 | "x": 0.5, 243 | "y": 0.5 244 | }, 245 | "_position": { 246 | "__type__": "cc.Vec3", 247 | "x": -411, 248 | "y": 0, 249 | "z": 0 250 | }, 251 | "_scale": { 252 | "__type__": "cc.Vec3", 253 | "x": 1, 254 | "y": 1, 255 | "z": 1 256 | }, 257 | "_rotationX": 0, 258 | "_rotationY": 0, 259 | "_quat": { 260 | "__type__": "cc.Quat", 261 | "x": 0, 262 | "y": 0, 263 | "z": 0, 264 | "w": 1 265 | }, 266 | "_skewX": 0, 267 | "_skewY": 0, 268 | "groupIndex": 0, 269 | "_id": "01sL99R09FH4Xh9s1jTC+J" 270 | }, 271 | { 272 | "__type__": "9ff8f4I8bxDtJup/IaxGuD7", 273 | "_name": "", 274 | "_objFlags": 0, 275 | "node": { 276 | "__id__": 5 277 | }, 278 | "_enabled": true, 279 | "string": "恭喜诛神丶青梦在XXX副本击败了XXXBOSS 获得了XXX头盔盔(1转)", 280 | "maxWidth": 450, 281 | "fontSize": 24, 282 | "lineHeight": 26, 283 | "eventComponentName": "LabelEvent", 284 | "atlas": { 285 | "__uuid__": "8181480b-3940-4089-8cf7-9fc57f7db235" 286 | }, 287 | "_id": "84FaNIaM9HqrWPZdaJB0Cp" 288 | }, 289 | { 290 | "__type__": "662a19D6n9Ih7OOIWjtc1Ya", 291 | "_name": "", 292 | "_objFlags": 0, 293 | "node": { 294 | "__id__": 5 295 | }, 296 | "_enabled": true, 297 | "_id": "7dr4yAyDNFc47TLahMNDAo" 298 | }, 299 | { 300 | "__type__": "cc.Canvas", 301 | "_name": "", 302 | "_objFlags": 0, 303 | "node": { 304 | "__id__": 2 305 | }, 306 | "_enabled": true, 307 | "_designResolution": { 308 | "__type__": "cc.Size", 309 | "width": 960, 310 | "height": 640 311 | }, 312 | "_fitWidth": false, 313 | "_fitHeight": true, 314 | "_id": "9dgK7lU2hNeqvJ8J83aMOC" 315 | } 316 | ] -------------------------------------------------------------------------------- /assets/Scene/main.fire.meta: -------------------------------------------------------------------------------- 1 | { 2 | "ver": "1.0.1", 3 | "uuid": "a10c3d2c-2d1f-4036-b01b-1af96cc46adf", 4 | "asyncLoadAssets": false, 5 | "autoReleaseAssets": false, 6 | "subMetas": {} 7 | } -------------------------------------------------------------------------------- /assets/Script.meta: -------------------------------------------------------------------------------- 1 | { 2 | "ver": "1.0.1", 3 | "uuid": "6f369d93-2ff1-44e4-89a1-f9b47da91d15", 4 | "isSubpackage": false, 5 | "subpackageName": "", 6 | "subMetas": {} 7 | } -------------------------------------------------------------------------------- /assets/Script/LabelEvent.ts: -------------------------------------------------------------------------------- 1 | // Learn TypeScript: 2 | // - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/typescript.html 3 | // - [English] http://www.cocos2d-x.org/docs/creator/manual/en/scripting/typescript.html 4 | // Learn Attribute: 5 | // - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/reference/attributes.html 6 | // - [English] http://www.cocos2d-x.org/docs/creator/manual/en/scripting/reference/attributes.html 7 | // Learn life-cycle callbacks: 8 | // - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/life-cycle-callbacks.html 9 | // - [English] http://www.cocos2d-x.org/docs/creator/manual/en/scripting/life-cycle-callbacks.html 10 | 11 | const { ccclass, property } = cc._decorator; 12 | 13 | @ccclass 14 | export default class NewClass extends cc.Component { 15 | 16 | 17 | start() { 18 | 19 | } 20 | nameClick(data) { 21 | cc.log('click', data) 22 | } 23 | // update (dt) {} 24 | } 25 | -------------------------------------------------------------------------------- /assets/Script/LabelEvent.ts.meta: -------------------------------------------------------------------------------- 1 | { 2 | "ver": "1.0.5", 3 | "uuid": "662a1f43-ea7f-4887-b38e-2168ed73561a", 4 | "isPlugin": false, 5 | "loadPluginInWeb": true, 6 | "loadPluginInNative": true, 7 | "loadPluginInEditor": false, 8 | "subMetas": {} 9 | } -------------------------------------------------------------------------------- /assets/Script/RichText.ts: -------------------------------------------------------------------------------- 1 | 2 | declare const utils; 3 | declare const require; 4 | const { ccclass, property } = cc._decorator; 5 | const HtmlTextParser = require('html-text-parser'); 6 | const _htmlTextParser = new HtmlTextParser(); 7 | interface TextData { 8 | text: string; 9 | style: RichStyle; 10 | } 11 | interface RichStyle { 12 | isImage?: boolean; 13 | src?: string; 14 | color?: string; 15 | event?: { click: string }; 16 | underline?: boolean; 17 | newline?: boolean;//换行 18 | } 19 | 20 | @ccclass 21 | export default class RichText extends cc.Component { 22 | 23 | @property 24 | string: string = ''; 25 | @property 26 | maxWidth: number = 0; 27 | @property 28 | fontSize: number = 20; 29 | @property 30 | lineHeight: number = 22; 31 | 32 | @property 33 | eventComponentName: string = ''; 34 | @property(cc.SpriteAtlas) 35 | atlas: cc.SpriteAtlas = null; 36 | // LIFE-CYCLE CALLBACKS: 37 | 38 | private sfs: [{ index: string }, cc.SpriteFrame]; 39 | 40 | //辅助排版 41 | private _nowX = 0; 42 | private _nowLine = 1; 43 | private _textData: TextData[] = [] 44 | private _heightFix = 0; // 高度修正 有图片高度可能超过lineheight 45 | private _mutilLineFix = 0; 46 | // onLoad () {} 47 | 48 | start() { 49 | this.node.anchorY = 1; 50 | let textData = _htmlTextParser.parse(this.string) as [TextData]; 51 | this._textData = textData; 52 | // cc.log(textData); 53 | this.sfs = {} as [{ index: string }, cc.SpriteFrame]; 54 | if (this.atlas) { 55 | this.atlas.getSpriteFrames().forEach(sf => { 56 | this.sfs[sf.name] = sf; 57 | }) 58 | } 59 | textData.forEach(d => { 60 | let node = this.dealData(d); 61 | // this.addItem(node); 62 | node.forEach(n => { 63 | this.addItemWithLayout(n, d.style) 64 | }) 65 | }) 66 | // cc.log(this) 67 | // this.scheduleOnce(_ => { 68 | // this.relayout(); 69 | this.resizeHeight(); 70 | // }) 71 | // this.node.on(cc.Node.EventType.TOUCH_END, e => { 72 | // this.debug(); 73 | // cc.log(this) 74 | // }) 75 | } 76 | 77 | dealData(data: TextData): Array { 78 | if (data.style && data.style.isImage) { 79 | return [this.makeSprite(data)]; 80 | } else { 81 | return this.makeLabel(data); 82 | } 83 | } 84 | makeLabel(textData: TextData): Array { 85 | let label = new cc.Node().addComponent(cc.Label); 86 | let style = textData.style || {}; 87 | label.string = textData.text; 88 | label.fontSize = this.fontSize; 89 | label.lineHeight = this.fontSize + 2; 90 | 91 | if (!style.color) { 92 | label.node.color = this.node.color; 93 | } else { 94 | label.node.color = new cc.Color().fromHEX(style.color) 95 | } 96 | //@ts-ignore 97 | label._enableUnderline(style.underline); 98 | if (style.event) { 99 | //添加事件 100 | label.node.on(cc.Node.EventType.TOUCH_END, e => { 101 | if (!this.eventComponentName) { 102 | cc.warn('未指定事件响应的component') 103 | return; 104 | } 105 | let eCmp = this.getComponent(this.eventComponentName); 106 | if (eCmp && eCmp[style.event.click]) { 107 | eCmp[style.event.click].call(eCmp); 108 | } else { 109 | cc.warn('cant find component ' + this.eventComponentName + ' for event ' + style.event.click) 110 | } 111 | }) 112 | } 113 | //计算width 114 | //@ts-ignore 115 | label._updateRenderData(true) 116 | // return [label.node]; 117 | return this._splitLabel(label.node, textData); 118 | } 119 | _splitLabel(node: cc.Node, data: TextData): Array { 120 | let labels: Array = []; 121 | if (this.maxWidth > 0) { 122 | let overWidth = (this._nowX + node.width) - this.maxWidth; 123 | if (overWidth > 0) { 124 | let _strLength = data.text.length; 125 | let _overStrs = Math.ceil(_strLength * (overWidth / this.maxWidth)); 126 | let str = data.text.slice(0, _strLength - _overStrs); 127 | let nextStr = data.text.slice(_strLength - _overStrs, data.text.length); 128 | labels.push(...this.makeLabel({ 129 | text: str, 130 | style: data.style 131 | })) 132 | labels.push(...this.makeLabel({ 133 | text: nextStr, 134 | style: data.style 135 | })) 136 | } else { 137 | labels.push(node) 138 | } 139 | } 140 | 141 | return labels; 142 | } 143 | makeSprite(textData: TextData): cc.Node { 144 | let sprite = new cc.Node().addComponent(cc.Sprite); 145 | let sf = this.sfs[textData.style.src] as cc.SpriteFrame; 146 | sprite.spriteFrame = sf; 147 | let size = sf.getOriginalSize(); 148 | sprite.node.width = size.width; 149 | sprite.node.height = size.height 150 | sprite.sizeMode = cc.Sprite.SizeMode.RAW; 151 | sprite.trim = false; 152 | return sprite.node; 153 | } 154 | addItemWithLayout(node: cc.Node, style: RichStyle) { 155 | let pos = this._getLastPos(); 156 | if (this.maxWidth > 0 && this._nowX + node.width > this.maxWidth || (style && style.newline)) { 157 | this._nextLine(); 158 | } 159 | 160 | pos.x = this._nowX + node.width / 2; 161 | pos.y = -this._nowLine * this.lineHeight + this.lineHeight / 2; 162 | this._nowX += node.width; 163 | 164 | 165 | 166 | node.position = pos; 167 | this.node.addChild(node); 168 | } 169 | 170 | addItem(node: cc.Node) { 171 | node.opacity = 0; 172 | this.node.addChild(node); 173 | } 174 | _nextLine() { 175 | this._nowX = 0; 176 | this._nowLine += 1; 177 | } 178 | 179 | //重新排版 180 | //TODO:分割label 181 | relayout() { 182 | this._nowX = 0; 183 | 184 | this.node.children.forEach((c, i) => { 185 | let x, y = 0; 186 | //处理换行 187 | if (this.maxWidth > 0) { 188 | 189 | if (this._nowX + c.width > this.maxWidth) { 190 | //需要换行 191 | this._nowLine += 1; 192 | //直接换 193 | this._nowX = 0; 194 | this._mutilLineFix += this._heightFix; 195 | this._heightFix = 0 196 | 197 | } else { 198 | 199 | } 200 | 201 | x = this._nowX + c.width / 2; 202 | y = this._nowLine * this.lineHeight; 203 | this._nowX += c.width; 204 | 205 | if (this._nowLine == 1) { //根据第一行决定width 206 | this.node.width += c.width; 207 | } 208 | let style = this._textData[i].style || {}; 209 | if (style.isImage) { 210 | if (c.height > this.lineHeight) { 211 | let fix = c.height - this.lineHeight; 212 | if (fix > this._heightFix) 213 | this._heightFix = fix; 214 | } 215 | } 216 | } else { 217 | x = this._nowX + c.width / 2; 218 | this._nowX += c.width; 219 | this.node.width += c.width; 220 | //fix height 221 | let style = this._textData[i].style || {}; 222 | if (style.isImage) { 223 | if (c.height > this.lineHeight) { 224 | let fix = c.height - this.lineHeight; 225 | if (fix > this._heightFix) 226 | this._heightFix = fix; 227 | } 228 | } 229 | } 230 | 231 | 232 | c.x = x; 233 | c.y = - y + this.lineHeight / 2; 234 | c.opacity = 255; 235 | }) 236 | } 237 | resizeHeight() { 238 | this.node.height = this._nowLine * this.lineHeight //+ (this._mutilLineFix || this._heightFix); 239 | } 240 | _getLastPos(): cc.Vec2 { 241 | let pos = new cc.Vec2(); 242 | let lastNode = this.node.children[this.node.children.length - 1] 243 | if (lastNode) 244 | pos = lastNode.position; 245 | return pos; 246 | } 247 | debug() { 248 | let graphic = this.node.addComponent(cc.Graphics); 249 | graphic.strokeColor = utils.getUIColor('green'); 250 | graphic.lineWidth = 2; 251 | graphic.moveTo(0, 0); 252 | graphic.circle(0, 0, 3); 253 | graphic.lineTo(this.node.width, 0) 254 | graphic.circle(this.node.width, 0, 3) 255 | 256 | graphic.lineTo(this.node.width, -this.node.height); 257 | graphic.circle(this.node.width, -this.node.height, 3); 258 | 259 | 260 | graphic.lineTo(0, -this.node.height); 261 | graphic.circle(0, -this.node.height, 3); 262 | 263 | graphic.lineTo(0, 0); 264 | 265 | graphic.stroke(); 266 | } 267 | 268 | //换行情况需要打断label 269 | 270 | // update (dt) {} 271 | } 272 | -------------------------------------------------------------------------------- /assets/Script/RichText.ts.meta: -------------------------------------------------------------------------------- 1 | { 2 | "ver": "1.0.5", 3 | "uuid": "9ff8fe08-f1bc-43b4-9ba9-fc86b11ae0fb", 4 | "isPlugin": false, 5 | "loadPluginInWeb": true, 6 | "loadPluginInNative": true, 7 | "loadPluginInEditor": false, 8 | "subMetas": {} 9 | } -------------------------------------------------------------------------------- /assets/Script/html-text-parser.js: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2013-2016 Chukong Technologies Inc. 3 | Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. 4 | 5 | https://www.cocos.com/ 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated engine source code (the "Software"), a limited, 9 | worldwide, royalty-free, non-assignable, revocable and non-exclusive license 10 | to use Cocos Creator solely to develop games on your target platforms. You shall 11 | not use Cocos Creator software for developing other software or tools that's 12 | used for developing games. You are not granted to publish, distribute, 13 | sublicense, and/or sell copies of Cocos Creator. 14 | 15 | The software or tools in this License Agreement are licensed, not sold. 16 | Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | ****************************************************************************/ 26 | 27 | var eventRegx = /^(click)(\s)*=|(param)(\s)*=/; 28 | var imageAttrReg = /(\s)*src(\s)*=|(\s)*height(\s)*=|(\s)*width(\s)*=|(\s)*click(\s)*=|(\s)*param(\s)*=/; 29 | /** 30 | * A utils class for parsing HTML texts. The parsed results will be an object array. 31 | */ 32 | var HtmlTextParser = function () { 33 | this._parsedObject = {}; 34 | this._specialSymbolArray = []; 35 | this._specialSymbolArray.push([/</g, '<']); 36 | this._specialSymbolArray.push([/>/g, '>']); 37 | this._specialSymbolArray.push([/&/g, '&']); 38 | this._specialSymbolArray.push([/"/g, '"']); 39 | this._specialSymbolArray.push([/'/g, '\'']); 40 | }; 41 | 42 | HtmlTextParser.prototype = { 43 | constructor: HtmlTextParser, 44 | parse: function (htmlString) { 45 | this._resultObjectArray = []; 46 | this._stack = []; 47 | 48 | var startIndex = 0; 49 | var length = htmlString.length; 50 | while (startIndex < length) { 51 | var tagBeginIndex = htmlString.indexOf('<', startIndex); 52 | if (tagBeginIndex < 0) { 53 | this._stack.pop(); 54 | this._processResult(htmlString.substring(startIndex)); 55 | startIndex = length; 56 | } else { 57 | this._processResult(htmlString.substring(startIndex, tagBeginIndex)); 58 | 59 | var tagEndIndex = htmlString.indexOf('>', startIndex); 60 | if (tagEndIndex === -1) { 61 | // cc.error('The HTML tag is invalid!'); 62 | tagEndIndex = tagBeginIndex; 63 | } else if (htmlString.charAt(tagBeginIndex + 1) === '\/') { 64 | this._stack.pop(); 65 | } else { 66 | this._addToStack(htmlString.substring(tagBeginIndex + 1, tagEndIndex)); 67 | } 68 | startIndex = tagEndIndex + 1; 69 | 70 | } 71 | } 72 | 73 | 74 | return this._resultObjectArray; 75 | }, 76 | 77 | _attributeToObject: function (attribute) { 78 | attribute = attribute.trim(); 79 | 80 | var obj = {}; 81 | var header = attribute.match(/^(color|size)(\s)*=/); 82 | var tagName; 83 | var nextSpace; 84 | var eventObj; 85 | var eventHanlderString; 86 | if (header) { 87 | tagName = header[0]; 88 | attribute = attribute.substring(tagName.length).trim(); 89 | if (attribute === "") return obj; 90 | 91 | //parse color 92 | nextSpace = attribute.indexOf(' '); 93 | switch (tagName[0]) { 94 | case 'c': 95 | if (nextSpace > -1) { 96 | obj.color = attribute.substring(0, nextSpace).trim(); 97 | } else { 98 | obj.color = attribute; 99 | } 100 | break; 101 | case 's': 102 | obj.size = parseInt(attribute); 103 | break; 104 | } 105 | 106 | //tag has event arguments 107 | if (nextSpace > -1) { 108 | eventHanlderString = attribute.substring(nextSpace + 1).trim(); 109 | eventObj = this._processEventHandler(eventHanlderString); 110 | obj.event = eventObj; 111 | } 112 | return obj; 113 | } 114 | 115 | header = attribute.match(/^(br(\s)*\/)/); 116 | if (header && header[0].length > 0) { 117 | tagName = header[0].trim(); 118 | if (tagName.startsWith("br") && tagName[tagName.length - 1] === "/") { 119 | obj.isNewLine = true; 120 | this._resultObjectArray.push({ text: "", style: { newline: true } }); 121 | return obj; 122 | } 123 | } 124 | 125 | header = attribute.match(/^(img(\s)*src(\s)*=[^>]+\/)/); 126 | if (header && header[0].length > 0) { 127 | tagName = header[0].trim(); 128 | if (tagName.startsWith("img") && tagName[tagName.length - 1] === "/") { 129 | header = attribute.match(imageAttrReg); 130 | var tagValue; 131 | var remainingArgument; 132 | var isValidImageTag = false; 133 | while (header) { 134 | //skip the invalid tags at first 135 | attribute = attribute.substring(attribute.indexOf(header[0])); 136 | tagName = attribute.substr(0, header[0].length); 137 | //remove space and = character 138 | remainingArgument = attribute.substring(tagName.length).trim(); 139 | nextSpace = remainingArgument.indexOf(' '); 140 | 141 | tagValue = (nextSpace > -1) ? remainingArgument.substr(0, nextSpace) : remainingArgument; 142 | tagName = tagName.replace(/[^a-zA-Z]/g, "").trim(); 143 | tagName = tagName.toLocaleLowerCase(); 144 | 145 | attribute = remainingArgument.substring(nextSpace).trim(); 146 | if (tagName === "src") { 147 | obj.isImage = true 148 | if (tagValue.endsWith('\/')) tagValue = tagValue.substring(0, tagValue.length - 1); 149 | if (tagValue.indexOf('\'') === 0) { 150 | isValidImageTag = true; 151 | tagValue = tagValue.substring(1, tagValue.length - 1); 152 | } else if (tagValue.indexOf('"') === 0) { 153 | isValidImageTag = true; 154 | tagValue = tagValue.substring(1, tagValue.length - 1); 155 | } 156 | obj.src = tagValue; 157 | } else if (tagName === "height") { 158 | obj.imageHeight = parseInt(tagValue); 159 | } else if (tagName === "width") { 160 | obj.imageWidth = parseInt(tagValue); 161 | } else if (tagName === "click") { 162 | obj.event = this._processEventHandler(tagName + "=" + tagValue); 163 | } 164 | 165 | if (obj.event && tagName === 'param') { 166 | obj.event.param = tagValue.replace(/^\"|\"$/g, ''); 167 | } 168 | 169 | header = attribute.match(imageAttrReg); 170 | } 171 | 172 | if (isValidImageTag && obj.isImage) { 173 | this._resultObjectArray.push({ text: "", style: obj }); 174 | } 175 | 176 | return {}; 177 | } 178 | } 179 | 180 | header = attribute.match(/^(outline(\s)*[^>]*)/); 181 | if (header) { 182 | attribute = header[0].substring("outline".length).trim(); 183 | var defaultOutlineObject = { color: "#ffffff", width: 1 }; 184 | if (attribute) { 185 | var outlineAttrReg = /(\s)*color(\s)*=|(\s)*width(\s)*=|(\s)*click(\s)*=|(\s)*param(\s)*=/; 186 | header = attribute.match(outlineAttrReg); 187 | var tagValue; 188 | while (header) { 189 | //skip the invalid tags at first 190 | attribute = attribute.substring(attribute.indexOf(header[0])); 191 | tagName = attribute.substr(0, header[0].length); 192 | //remove space and = character 193 | remainingArgument = attribute.substring(tagName.length).trim(); 194 | nextSpace = remainingArgument.indexOf(' '); 195 | if (nextSpace > -1) { 196 | tagValue = remainingArgument.substr(0, nextSpace); 197 | } else { 198 | tagValue = remainingArgument; 199 | } 200 | tagName = tagName.replace(/[^a-zA-Z]/g, "").trim(); 201 | tagName = tagName.toLocaleLowerCase(); 202 | 203 | attribute = remainingArgument.substring(nextSpace).trim(); 204 | if (tagName === "click") { 205 | obj.event = this._processEventHandler(tagName + "=" + tagValue); 206 | } else if (tagName === "color") { 207 | defaultOutlineObject.color = tagValue; 208 | } else if (tagName === "width") { 209 | defaultOutlineObject.width = parseInt(tagValue); 210 | } 211 | 212 | if (obj.event && tagName === 'param') { 213 | obj.event.param = tagValue.replace(/^\"|\"$/g, ''); 214 | } 215 | 216 | header = attribute.match(outlineAttrReg); 217 | } 218 | } 219 | obj.outline = defaultOutlineObject; 220 | } 221 | 222 | header = attribute.match(/^(on|u|b|i)(\s)*/); 223 | if (header && header[0].length > 0) { 224 | tagName = header[0]; 225 | attribute = attribute.substring(tagName.length).trim(); 226 | switch (tagName[0]) { 227 | case 'u': 228 | obj.underline = true; 229 | break; 230 | case 'i': 231 | obj.italic = true; 232 | break; 233 | case 'b': 234 | obj.bold = true; 235 | break; 236 | } 237 | if (attribute === "") { 238 | return obj; 239 | } 240 | eventObj = this._processEventHandler(attribute); 241 | obj.event = eventObj; 242 | } 243 | 244 | return obj; 245 | }, 246 | 247 | _processEventHandler: function (eventString) { 248 | var index = 0; 249 | var obj = {}; 250 | var eventNames = eventString.match(eventRegx); 251 | var isValidTag = false; 252 | while (eventNames) { 253 | var eventName = eventNames[0]; 254 | var eventValue = ""; 255 | isValidTag = false; 256 | eventString = eventString.substring(eventName.length).trim(); 257 | if (eventString.charAt(0) === "\"") { 258 | index = eventString.indexOf("\"", 1); 259 | if (index > -1) { 260 | eventValue = eventString.substring(1, index).trim(); 261 | isValidTag = true; 262 | } 263 | index++; 264 | } else if (eventString.charAt(0) === "\'") { 265 | index = eventString.indexOf('\'', 1); 266 | if (index > -1) { 267 | eventValue = eventString.substring(1, index).trim(); 268 | isValidTag = true; 269 | } 270 | index++; 271 | } else { 272 | //skip the invalid attribute value 273 | var match = eventString.match(/(\S)+/); 274 | if (match) { 275 | eventValue = match[0]; 276 | } else { 277 | eventValue = ""; 278 | } 279 | index = eventValue.length; 280 | } 281 | 282 | if (isValidTag) { 283 | eventName = eventName.substring(0, eventName.length - 1).trim(); 284 | obj[eventName] = eventValue; 285 | } 286 | 287 | eventString = eventString.substring(index).trim(); 288 | eventNames = eventString.match(eventRegx); 289 | } 290 | 291 | return obj; 292 | }, 293 | 294 | _addToStack: function (attribute) { 295 | var obj = this._attributeToObject(attribute); 296 | 297 | if (this._stack.length === 0) { 298 | this._stack.push(obj); 299 | } else { 300 | if (obj.isNewLine || obj.isImage) { 301 | return; 302 | } 303 | //for nested tags 304 | var previousTagObj = this._stack[this._stack.length - 1]; 305 | for (var key in previousTagObj) { 306 | if (!(obj[key])) { 307 | obj[key] = previousTagObj[key]; 308 | } 309 | } 310 | this._stack.push(obj); 311 | } 312 | }, 313 | 314 | _processResult: function (value) { 315 | if (value === "") { 316 | return; 317 | } 318 | 319 | value = this._escapeSpecialSymbol(value); 320 | if (this._stack.length > 0) { 321 | this._resultObjectArray.push({ text: value, style: this._stack[this._stack.length - 1] }); 322 | } else { 323 | this._resultObjectArray.push({ text: value }); 324 | } 325 | }, 326 | 327 | _escapeSpecialSymbol: function (str) { 328 | for (var i = 0; i < this._specialSymbolArray.length; ++i) { 329 | var key = this._specialSymbolArray[i][0]; 330 | var value = this._specialSymbolArray[i][1]; 331 | 332 | str = str.replace(key, value); 333 | } 334 | return str; 335 | } 336 | }; 337 | 338 | if (CC_TEST) { 339 | cc._Test.HtmlTextParser = HtmlTextParser; 340 | } 341 | 342 | module.exports = HtmlTextParser; -------------------------------------------------------------------------------- /assets/Script/html-text-parser.js.meta: -------------------------------------------------------------------------------- 1 | { 2 | "ver": "1.0.5", 3 | "uuid": "579aa95c-5045-4007-9c53-c3cea466b7fe", 4 | "isPlugin": false, 5 | "loadPluginInWeb": true, 6 | "loadPluginInNative": true, 7 | "loadPluginInEditor": false, 8 | "subMetas": {} 9 | } -------------------------------------------------------------------------------- /assets/Texture.meta: -------------------------------------------------------------------------------- 1 | { 2 | "ver": "1.0.1", 3 | "uuid": "8afe38f3-e5bf-424e-ae42-ade4ac6ddc49", 4 | "isSubpackage": false, 5 | "subpackageName": "", 6 | "subMetas": {} 7 | } -------------------------------------------------------------------------------- /assets/Texture/faces.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | frames 6 | 7 | face_01.png 8 | 9 | aliases 10 | 11 | spriteOffset 12 | {0,0} 13 | spriteSize 14 | {34,34} 15 | spriteSourceSize 16 | {34,34} 17 | textureRect 18 | {{1,1},{34,34}} 19 | textureRotated 20 | 21 | 22 | face_02.png 23 | 24 | aliases 25 | 26 | spriteOffset 27 | {0,0} 28 | spriteSize 29 | {34,34} 30 | spriteSourceSize 31 | {34,34} 32 | textureRect 33 | {{37,1},{34,34}} 34 | textureRotated 35 | 36 | 37 | face_03.png 38 | 39 | aliases 40 | 41 | spriteOffset 42 | {0,0} 43 | spriteSize 44 | {34,34} 45 | spriteSourceSize 46 | {34,34} 47 | textureRect 48 | {{73,1},{34,34}} 49 | textureRotated 50 | 51 | 52 | 53 | metadata 54 | 55 | format 56 | 3 57 | pixelFormat 58 | RGBA8888 59 | premultiplyAlpha 60 | 61 | realTextureFileName 62 | faces.png 63 | size 64 | {108,36} 65 | smartupdate 66 | $TexturePacker:SmartUpdate:dbea3bdea6dcf32c962365186fb75bff:b2c382a309115c98c5ed92bedcdce115:215e6747958b57305900da20c1195263$ 67 | textureFileName 68 | faces.png 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /assets/Texture/faces.plist.meta: -------------------------------------------------------------------------------- 1 | { 2 | "ver": "1.2.4", 3 | "uuid": "8181480b-3940-4089-8cf7-9fc57f7db235", 4 | "rawTextureUuid": "1d5c05a2-b31b-4f78-ac4a-9b5bdd304743", 5 | "size": { 6 | "width": 108, 7 | "height": 36 8 | }, 9 | "type": "Texture Packer", 10 | "subMetas": { 11 | "face_01.png": { 12 | "ver": "1.0.4", 13 | "uuid": "9cc43ccf-8fe0-474d-9bdd-083e40ee0a07", 14 | "rawTextureUuid": "1d5c05a2-b31b-4f78-ac4a-9b5bdd304743", 15 | "trimType": "auto", 16 | "trimThreshold": 1, 17 | "rotated": false, 18 | "offsetX": 0, 19 | "offsetY": 0, 20 | "trimX": 1, 21 | "trimY": 1, 22 | "width": 34, 23 | "height": 34, 24 | "rawWidth": 34, 25 | "rawHeight": 34, 26 | "borderTop": 0, 27 | "borderBottom": 0, 28 | "borderLeft": 0, 29 | "borderRight": 0, 30 | "spriteType": "normal", 31 | "subMetas": {} 32 | }, 33 | "face_02.png": { 34 | "ver": "1.0.4", 35 | "uuid": "8f6038fe-7324-4c34-801c-c78b4b63775e", 36 | "rawTextureUuid": "1d5c05a2-b31b-4f78-ac4a-9b5bdd304743", 37 | "trimType": "auto", 38 | "trimThreshold": 1, 39 | "rotated": false, 40 | "offsetX": 0, 41 | "offsetY": 0, 42 | "trimX": 37, 43 | "trimY": 1, 44 | "width": 34, 45 | "height": 34, 46 | "rawWidth": 34, 47 | "rawHeight": 34, 48 | "borderTop": 0, 49 | "borderBottom": 0, 50 | "borderLeft": 0, 51 | "borderRight": 0, 52 | "spriteType": "normal", 53 | "subMetas": {} 54 | }, 55 | "face_03.png": { 56 | "ver": "1.0.4", 57 | "uuid": "0668c9a3-f03c-4710-b232-09f392a92abd", 58 | "rawTextureUuid": "1d5c05a2-b31b-4f78-ac4a-9b5bdd304743", 59 | "trimType": "auto", 60 | "trimThreshold": 1, 61 | "rotated": false, 62 | "offsetX": 0, 63 | "offsetY": 0, 64 | "trimX": 73, 65 | "trimY": 1, 66 | "width": 34, 67 | "height": 34, 68 | "rawWidth": 34, 69 | "rawHeight": 34, 70 | "borderTop": 0, 71 | "borderBottom": 0, 72 | "borderLeft": 0, 73 | "borderRight": 0, 74 | "spriteType": "normal", 75 | "subMetas": {} 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /assets/Texture/faces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yutou527/CocosCreator_RichTextFix/fdb23619fdabf0abcaaa00a34c101be6073006ee/assets/Texture/faces.png -------------------------------------------------------------------------------- /assets/Texture/faces.png.meta: -------------------------------------------------------------------------------- 1 | { 2 | "ver": "2.2.0", 3 | "uuid": "1d5c05a2-b31b-4f78-ac4a-9b5bdd304743", 4 | "type": "sprite", 5 | "wrapMode": "clamp", 6 | "filterMode": "bilinear", 7 | "premultiplyAlpha": false, 8 | "subMetas": { 9 | "faces": { 10 | "ver": "1.0.4", 11 | "uuid": "d1ec1703-da19-4138-a141-66c4cee21552", 12 | "rawTextureUuid": "1d5c05a2-b31b-4f78-ac4a-9b5bdd304743", 13 | "trimType": "auto", 14 | "trimThreshold": 1, 15 | "rotated": false, 16 | "offsetX": 0, 17 | "offsetY": 0, 18 | "trimX": 0, 19 | "trimY": 0, 20 | "width": 108, 21 | "height": 36, 22 | "rawWidth": 108, 23 | "rawHeight": 36, 24 | "borderTop": 0, 25 | "borderBottom": 0, 26 | "borderLeft": 0, 27 | "borderRight": 0, 28 | "subMetas": {} 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /doc/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yutou527/CocosCreator_RichTextFix/fdb23619fdabf0abcaaa00a34c101be6073006ee/doc/preview.png -------------------------------------------------------------------------------- /doc/preview2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yutou527/CocosCreator_RichTextFix/fdb23619fdabf0abcaaa00a34c101be6073006ee/doc/preview2.jpg -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "experimentalDecorators": true 6 | }, 7 | "exclude": [ 8 | "node_modules", 9 | ".vscode", 10 | "library", 11 | "local", 12 | "settings", 13 | "temp" 14 | ] 15 | } -------------------------------------------------------------------------------- /project.json: -------------------------------------------------------------------------------- 1 | { 2 | "engine": "cocos-creator-js", 3 | "packages": "packages" 4 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### 用Label和Sprite简单实现的一个RichText 2 | 3 | 由于CocosCreator更新2.0之后RichText性能极低,复杂点的聊天界面在低性能手机上几乎无法正正常使用,初始化十几条要卡顿秒级,所以使用了RichText相同的parser,意味着如果你以前用的是RichText,可以无缝切换至此组件。 4 | 5 | ![](./doc/preview.png) 6 | ### 更新 支持了截断文字的换行方式 7 | ![](./doc/preview2.jpg) 8 | 9 | --- 10 | ### 已实现 11 | 12 | - 文字颜色 13 | - 文字下划线 14 | - 点击事件 15 | - 图片 16 | - 文字换行 17 | - 文字截断换行 18 | 19 | 20 | -------------------------------------------------------------------------------- /settings/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "start-scene": "current", 3 | "group-list": [ 4 | "default" 5 | ], 6 | "collision-matrix": [ 7 | [ 8 | true 9 | ] 10 | ], 11 | "excluded-modules": [], 12 | "last-module-event-record-time": 0, 13 | "design-resolution-width": 960, 14 | "design-resolution-height": 640, 15 | "fit-width": false, 16 | "fit-height": true, 17 | "use-project-simulator-setting": false, 18 | "simulator-orientation": false, 19 | "use-customize-simulator": false, 20 | "simulator-resolution": { 21 | "width": 960, 22 | "height": 640 23 | }, 24 | "assets-sort-type": "name", 25 | "facebook": { 26 | "enable": false, 27 | "appID": "", 28 | "live": { 29 | "enable": false 30 | }, 31 | "audience": { 32 | "enable": false 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /settings/services.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": [ 3 | { 4 | "service_id": "235", 5 | "service_name": "Cocos Analytics", 6 | "service_icon": "https://account.cocos.com/client/3f8f31ccf66995e183044f167c092395.png", 7 | "service_desc": "提供最核心最基本的数据、标准化界面功能简洁易用、数据准确性最好", 8 | "service_title": "精准了解游戏的新增、活跃、留存、付费等数据", 9 | "service_guide_url": "https://n-analytics.cocos.com/docs/", 10 | "service_sample_url": "https://github.com/cocos-creator/tutorial-dark-slash/tree/analytics", 11 | "service_dev_url": "http://analytics.cocos.com/realtime/jump_to/", 12 | "service_type": "3", 13 | "service_type_zh": "公司和个人游戏", 14 | "support_platform": [ 15 | "Android", 16 | "iOS", 17 | "HTML5" 18 | ], 19 | "package_download_url": "https://download.cocos.com/CocosServices/plugins/service-analytics/1.2.0_2.1.0.zip", 20 | "package_version_desc": "更新日期:2019/6/10
\n
更新说明:
\n1、优化SDK,修复H5-SDK 与多个小游戏平台适配问题,删除和优化init事件无用接口
\n2、如有相关问题咨询或者需求, 可以联系我们技术支持邮箱 support-cocos@cocos.com", 21 | "service_component_name": "service-analytics", 22 | "package_versions": [ 23 | "1.2.1_2.1.0", 24 | "1.2.0_2.1.0", 25 | "1.1.7_2.0.3", 26 | "1.1.6_2.0.1_2.0.2", 27 | "1.1.5_2.0.1", 28 | "1.1.4_2.0.1", 29 | "1.1.3_2.0.1", 30 | "1.1.2_2.0.0", 31 | "1.0.0_1.0.5" 32 | ], 33 | "build_platform": [], 34 | "require_verify": 0, 35 | "service_price": "", 36 | "service_protocol": "游戏首次开启该服务时,Cocos会后台通知服务方为游戏开通服务并初始化参数,服务方根据需要可能会获取您的Cocos账户信息,包括账户基本资料、游戏基本资料、账户余额等,点击确认开通按钮即视为您同意该服务访问您的账户信息,详见《Cocos用户服务协议》《Cocos隐私政策》" 37 | }, 38 | { 39 | "service_id": "241", 40 | "service_name": "Matchvs", 41 | "service_icon": "https://account.cocos.com/client/14406719a07eb3d714d36e5edc6e06fa.png", 42 | "service_desc": "通过SDK接入快速实现联网功能、帧同步、国内外多节点、服务器独立部署、gameServer自定义游戏服务端逻辑。", 43 | "service_title": "专业成熟的移动游戏联网与服务端解决方案", 44 | "service_guide_url": "http://doc.matchvs.com/QuickStart/QuickStart-CocosCreator", 45 | "service_sample_url": "http://www.matchvs.com/serviceCourse", 46 | "service_dev_url": "http://www.matchvs.com/cocosLogin", 47 | "service_type": "3", 48 | "service_type_zh": "公司和个人游戏", 49 | "support_platform": [ 50 | "Android", 51 | "iOS", 52 | "HTML5" 53 | ], 54 | "package_download_url": "https://download.cocos.com/CocosServices/plugins/service-matchvs/1.0.10_3.7.9.10.zip", 55 | "package_version_desc": "

更新日期: 2019/9/12\n更新内容:\n1.多语言支持\n2.SDK日常更新

", 56 | "service_component_name": "service-matchvs", 57 | "package_versions": [ 58 | "1.0.9_3.7.9.9", 59 | "1.0.7_3.7.9.6", 60 | "1.0.6_3.7.9.2", 61 | "1.0.5_3.7.7.3", 62 | "1.0.3_3.7.6.4", 63 | "1.0.10_3.7.9.10" 64 | ], 65 | "build_platform": [], 66 | "require_verify": 0, 67 | "service_price": "该服务按使用量计费,计费规则,所产生的费用将由第三方从您的 Cocos 账户余额 中扣除。", 68 | "service_protocol": "游戏首次开启该服务时,Cocos会后台通知服务方为游戏开通服务并初始化参数,服务方根据需要可能会获取您的Cocos账户信息,包括账户基本资料、游戏基本资料、账户余额等,点击确认开通按钮即视为您同意该服务访问您的账户信息,详见《Cocos用户服务协议》《Cocos隐私政策》" 69 | }, 70 | { 71 | "service_id": "242", 72 | "service_name": "Agora Voice", 73 | "service_icon": "https://account.cocos.com/uploads/client_icon/2019-07-16/273952d155b4cdb72d2b1bc61de91ade.png", 74 | "service_desc": "稳定、低耗、76ms超低延时、全球200+数据中心覆盖;变声器、超高音质、听声辩位等丰富玩法极速接入;全平台支持:Android、iOS、Web。", 75 | "service_title": "游戏内置实时语音", 76 | "service_guide_url": "https://docs.agora.io/cn/Interactive Gaming/game_c?platform=Cocos Creator", 77 | "service_sample_url": "https://github.com/AgoraIO/Voice-Call-for-Mobile-Gaming/tree/master/Basic-Voice-Call-for-Gaming/Hello-CocosCreator-Voice-Agora", 78 | "service_dev_url": "https://sso.agora.io/api/oauth/cocos/login", 79 | "service_type": "3", 80 | "service_type_zh": "公司和个人游戏", 81 | "support_platform": [ 82 | "Android", 83 | "iOS", 84 | "HTML5" 85 | ], 86 | "package_download_url": "https://download.cocos.com/CocosServices/plugins/service-agora/1.0.2_2.2.3.20_2.5.2.zip", 87 | "package_version_desc": "更新日期:2019/06/27
\n
更新内容:
\n1、修复部分BUG
\n2、代码优化", 88 | "service_component_name": "service-agora", 89 | "package_versions": [ 90 | "1.0.2_2.2.3.20_2.5.2", 91 | "1.0.1_2.2.3.20_2.5.2" 92 | ], 93 | "build_platform": [], 94 | "require_verify": 1, 95 | "service_price": "该服务按使用量计费,计费规则,所产生的费用将由第三方从您的 Cocos 账户余额 中扣除。", 96 | "service_protocol": "游戏首次开启该服务时,Cocos会后台通知服务方为游戏开通服务并初始化参数,服务方根据需要可能会获取您的Cocos账户信息,包括账户基本资料、游戏基本资料、账户余额等,点击确认开通按钮即视为您同意该服务访问您的账户信息,详见《Cocos用户服务协议》《Cocos隐私政策》" 97 | } 98 | ], 99 | "game": { 100 | "name": "未知游戏", 101 | "appid": "UNKNOW" 102 | } 103 | } --------------------------------------------------------------------------------