Skip to content

Commit f6f69ad

Browse files
committed
🎈 perf: 优化代码,支持移动端拖拽嵌入
1 parent 7f79b48 commit f6f69ad

File tree

1 file changed

+131
-67
lines changed

1 file changed

+131
-67
lines changed

game/embed/src/index.ts

Lines changed: 131 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ export default GameEmbed;
219219
function registerGameButton() {
220220
// 在 windows 注入游戏按钮样式
221221
const style = document.createElement('style');
222-
const rootSelector = `a[href*="${scriptSrc.hostname}/#/"]`
222+
const rootSelector = `a[href*="${scriptSrc.hostname}/#/"]`;
223223
style.innerHTML = `
224224
${rootSelector}, ${rootSelector}>span>span {
225225
display: inline-flex;
@@ -269,6 +269,55 @@ function registerGameButton() {
269269
align-items: center;
270270
justify-content: center;
271271
}
272+
.game-embed-iframe {
273+
width: 100%;
274+
height: 100%;
275+
border: none;
276+
border-radius: 8px;
277+
}
278+
.game-embed-container {
279+
position: fixed;
280+
background-color: rgba(0, 0, 0, 0.5);
281+
z-index: 1001;
282+
display: flex;
283+
flex-direction: column;
284+
align-items: center;
285+
justify-content: center;
286+
resize: both;
287+
overflow: hidden;
288+
border-radius: 8px;
289+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
290+
min-width: 100px;
291+
min-height: 100px;
292+
}
293+
.game-embed-close-button {
294+
background: transparent;
295+
border: none;
296+
color: white;
297+
font-size: 24px;
298+
cursor: pointer;
299+
z-index: 1005;
300+
}
301+
.game-embed-drag-handle {
302+
position: relative;
303+
width: 100%;
304+
height: 30px;
305+
cursor: grab;
306+
background-color: rgba(0, 0, 0, 0.1);
307+
border-top-left-radius: 8px;
308+
border-top-right-radius: 8px;
309+
z-index: 1002;
310+
text-align: right;
311+
user-select: none;
312+
}
313+
.game-embed-open-button {
314+
background: transparent;
315+
border: none;
316+
color: white;
317+
font-size: 18px;
318+
cursor: pointer;
319+
z-index: 1003;
320+
}
272321
`;
273322
document.head.appendChild(style);
274323

@@ -300,120 +349,135 @@ interface IRect {
300349
height: number;
301350
}
302351

303-
// 创建一个浮动的可调整窗口大小的 iframe 来加载游戏房间, 默认大小为 800x600
352+
// 创建一个浮动的可调整窗口大小的 iframe 来加载游戏房间, 默认大小为 300 x 400
304353
function appendGameViewIframe(roomUrl: string) {
354+
const minWidth = 100;
355+
const minHeight = 100;
305356
let iframe = document.getElementById('game-view-iframe') as HTMLIFrameElement | null;
306357
let rect: IRect | null = JSON.parse(localStorage.getItem('game-view-iframe-rect') || 'null');
307358

359+
// 辅助函数:保存当前位置和尺寸到localStorage
360+
const saveRect = (container: HTMLElement) => {
361+
const currentRect: IRect = {
362+
top: parseFloat(container.style.top.replace('px', '')) || 0,
363+
left: parseFloat(container.style.left.replace('px', '')) || 0,
364+
width: Math.max(container.offsetWidth, minWidth),
365+
height: Math.max(container.offsetHeight, minHeight)
366+
};
367+
localStorage.setItem('game-view-iframe-rect', JSON.stringify(currentRect));
368+
};
369+
370+
// 辅助函数:应用边界检查
371+
const applyBounds = (container: HTMLElement) => {
372+
const rect = container.getBoundingClientRect();
373+
const maxX = window.innerWidth - rect.width;
374+
const maxY = window.innerHeight - rect.height;
375+
const newLeft = Math.max(0, Math.min(parseFloat(container.style.left.replace('px', '')), maxX));
376+
const newTop = Math.max(0, Math.min(parseFloat(container.style.top.replace('px', '')), maxY));
377+
container.style.left = newLeft + 'px';
378+
container.style.top = newTop + 'px';
379+
};
380+
308381
if (!iframe) {
309382
iframe = document.createElement('iframe');
310383
iframe.id = 'game-view-iframe';
311-
iframe.style.width = '100%';
312-
iframe.style.height = '100%';
313-
iframe.style.border = 'none';
314-
iframe.style.borderRadius = '8px';
384+
iframe.className = 'game-embed-iframe';
315385
iframe.src = roomUrl;
316386

317387
const container = document.createElement('div');
318388
container.id = 'game-view-container';
319-
container.style.position = 'fixed';
320-
container.style.top = (rect?.top ?? (window.innerHeight - 320) / 2) + 'px';
389+
container.className = 'game-embed-container';
390+
// 保留动态样式(位置和尺寸)
391+
container.style.top = (rect?.top ?? (window.innerHeight - 320) / 2) + 'px';
321392
container.style.left = (rect?.left ?? (window.innerWidth - 420) / 2) + 'px';
322-
container.style.width = (rect?.width || 300) + 'px';
323-
container.style.height = (rect?.height || 400) + 'px';
324-
container.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
325-
container.style.zIndex = '1001';
326-
container.style.display = 'flex';
327-
container.style.flexDirection = 'column';
328-
container.style.alignItems = 'center';
329-
container.style.justifyContent = 'center';
330-
container.style.resize = 'both';
331-
container.style.overflow = 'hidden';
332-
container.style.borderRadius = '8px';
333-
container.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.3)';
393+
container.style.width = Math.max(rect?.width || 300, minWidth) + 'px';
394+
container.style.height = Math.max(rect?.height || 400, minHeight) + 'px';
334395

335396
// 关闭按钮
336397
const closeButton = document.createElement('button');
398+
closeButton.className = 'game-embed-close-button';
337399
closeButton.innerText = '×';
338-
closeButton.style.background = 'transparent';
339-
closeButton.style.border = 'none';
340-
closeButton.style.color = 'white';
341-
closeButton.style.fontSize = '24px';
342-
closeButton.style.cursor = 'pointer';
343-
closeButton.style.zIndex = '1005';
344400
closeButton.addEventListener('click', () => {
345-
document.body.removeChild(container);
346-
localStorage.setItem('game-view-iframe-rect', JSON.stringify({
347-
top: Number(container.style.top.replace('px', '')),
348-
left: Number(container.style.left.replace('px', '')),
349-
width: container.offsetWidth,
350-
height: container.offsetHeight
351-
}));
401+
if (document.body.contains(container)) {
402+
document.body.removeChild(container);
403+
saveRect(container);
404+
}
352405
});
353406

354407
// 拖拽控制标签
355408
const dragHandle = document.createElement('div');
356-
dragHandle.style.position = 'relative';
357-
dragHandle.style.width = '100%';
358-
dragHandle.style.height = '30px';
359-
dragHandle.style.cursor = 'grab';
360-
dragHandle.style.backgroundColor = 'rgba(0, 0, 0, 0.1)';
361-
dragHandle.style.borderTopLeftRadius = '8px';
362-
dragHandle.style.borderTopRightRadius = '8px';
363-
dragHandle.style.zIndex = '1002';
364-
dragHandle.style.textAlign = 'right';
365-
dragHandle.style.userSelect = 'none';
409+
dragHandle.className = 'game-embed-drag-handle';
366410
container.appendChild(dragHandle);
367411

368412
// 打开新窗口按钮
369-
const openButton = document.createElement('button');
413+
const openButton = document.createElement('button');
414+
openButton.className = 'game-embed-open-button';
370415
openButton.innerText = '↗';
371-
openButton.style.background = 'transparent';
372-
openButton.style.border = 'none';
373-
openButton.style.color = 'white';
374-
openButton.style.fontSize = '18px';
375-
openButton.style.cursor = 'pointer';
376-
openButton.style.zIndex = '1003';
377416
openButton.title = '在新窗口打开游戏';
378417
openButton.addEventListener('click', () => {
379418
window.open(roomUrl.replace(/\/l\//, '/r/'), '_blank');
380419
});
381420
dragHandle.appendChild(openButton);
382421
dragHandle.appendChild(closeButton);
383422

384-
// 允许拖动窗口
423+
// 拖动状态变量
385424
let isDragging = false;
386425
let dragOffsetX = 0;
387426
let dragOffsetY = 0;
388427

389-
dragHandle.addEventListener('mousedown', (e) => {
390-
if (e.target === closeButton) return; // 不允许拖动关闭按钮
428+
// 鼠标拖动事件(桌面端)
429+
const startDrag = (clientX: number, clientY: number) => {
391430
isDragging = true;
392-
dragOffsetX = e.clientX - container.offsetLeft;
393-
dragOffsetY = e.clientY - container.offsetTop;
431+
dragOffsetX = clientX - container.offsetLeft;
432+
dragOffsetY = clientY - container.offsetTop;
394433
container.style.cursor = 'grabbing';
395-
});
434+
};
396435

397-
document.addEventListener('mousemove', (e) => {
436+
const doDrag = (clientX: number, clientY: number) => {
398437
if (isDragging) {
399-
container.style.left = (e.clientX - dragOffsetX) + 'px';
400-
container.style.top = (e.clientY - dragOffsetY) + 'px';
438+
container.style.left = (clientX - dragOffsetX) + 'px';
439+
container.style.top = (clientY - dragOffsetY) + 'px';
401440
}
402-
});
441+
};
403442

404-
document.addEventListener('mouseup', () => {
443+
const endDrag = () => {
405444
if (isDragging) {
406445
isDragging = false;
407446
container.style.cursor = 'default';
408-
localStorage.setItem('game-view-iframe-rect', JSON.stringify({
409-
top: Number(container.style.top.replace('px', '')),
410-
left: Number(container.style.left.replace('px', '')),
411-
width: container.offsetWidth,
412-
height: container.offsetHeight
413-
}));
447+
applyBounds(container);
448+
saveRect(container);
414449
}
450+
};
451+
452+
// 鼠标事件
453+
dragHandle.addEventListener('mousedown', (e) => {
454+
if (e.target === closeButton) return;
455+
startDrag(e.clientX, e.clientY);
456+
});
457+
458+
document.addEventListener('mousemove', (e) => {
459+
doDrag(e.clientX, e.clientY);
415460
});
416461

462+
document.addEventListener('mouseup', endDrag);
463+
464+
// 触摸事件(移动端支持)
465+
dragHandle.addEventListener('touchstart', (e) => {
466+
if (e.target === closeButton) return;
467+
const touch = e.touches[0];
468+
startDrag(touch.clientX, touch.clientY);
469+
}, { passive: false });
470+
471+
document.addEventListener('touchmove', (e) => {
472+
if (isDragging) {
473+
e.preventDefault(); // 防止页面滚动
474+
const touch = e.touches[0];
475+
doDrag(touch.clientX, touch.clientY);
476+
}
477+
}, { passive: false });
478+
479+
document.addEventListener('touchend', endDrag);
480+
417481
container.appendChild(iframe);
418482
document.body.appendChild(container);
419483
}

0 commit comments

Comments
 (0)