1. 開発プロセス
開発中に試してみた
1. スティッキー レイアウト、固定スティッキー配置、および固定配置を使用しますが、次のようないくつかの問題があります。
1.1. Android スマートフォンに付属のブラウザ (oppo や vivo など) ではスティッキー レイアウトが失敗し、ヘッダーが修正されません。
1.2. スライド処理中、テーブルにはドラッグ効果が生じます (ドキュメント フローから外れているように見え、空白スペースがありますが、手を放すと元に戻ります)
1.3. 1.2 に基づいて、固定の最初の列が元の位置に配置され、ページが 3 つの部分に分かれているように見えます。
2. 最終計画
テーブルをヘッダー、最初の列、テーブル本体の 3 つの部分に分割します。テーブル本体のスクロール効果はoverfloe:scrollにより実現され、ヘッダーと最初の列はテーブル本体のスクロールイベントを通じて絶対位置のオフセット値を動的に設定することで実現されます。
2. 実現
1、HTML
<div class="content-table" v-show="tabActive == 2">
<!-- 表头 -->
<div class="rowHeader">
<table class="rowHeader-table" id="rowHeader-table">
<tr>
<td class="td seller_name tableH_fixed-left">销售员</td>
<td class="td seller_phone">手机号</td>
<td class="td one_branch">一级分校</td>
<td class="td two_branch">二级分校</td>
<td class="td total_uv double">累计访问人数</td>
<td class="td total_time double">累计访问时长(min)</td>
<td class="td add_uv double">昨日新增访问人数</td>
<td class="td add_time double">昨日新增访问时长(min)</td>
</tr>
</table>
</div>
<!-- 列头 -->
<div class="columnHeader">
<table class="columnHeader-table" id="columnHeader-table">
<tr v-for="(item, index) in sellersList" :key="index">
<td class="td seller_name">
{
{ item.sellerName }}
</td>
</tr>
</table>
</div>
//表格滚动主体
<div class="tableBody" id="tableBody" @scroll="scrollFn($event)">
<table class="tableBody-table" id="tableBody-table">
<tr v-for="(item, index) in sellersList" :key="index">
<td class="td seller_phone">{
{ item.sellerPhone }}</td>
<td class="td one_branch">{
{ item.branchName }}</td>
<td class="td two_branch">{
{ item.secondaryBranchName }}</td>
<td class="td total_uv">{
{ item.totalUv }}</td>
<td class="td total_time">{
{ item.totalVisitTime.toFixed(2) }}</td>
<td class="td add_uv">{
{ item.increasedUv }}</td>
<td class="td add_time">{
{ item.increasedVisitTime.toFixed(2) }}</td>
</tr>
</table>
</div>
</div>
</div>
2、CSS
.content-table {
position: absolute;
left: 16px;
top: 234px;
width: 343px;
height: calc(100% - 268px);
// 表头
.rowHeader {
position: relative;
width: 343px;
max-width: 343px;
overflow: hidden;
left: 0px;
// 表头table
.rowHeader-table {
position: relative;
width: fit-content;
height: 52px;
border-spacing: 0px;
.tableH_fixed-left {
position: sticky;
left: 0;
}
td {
display: flex;
justify-content: center;
align-items: center;
height: 52px;
color: #fff;
font-size: 14px;
background: #5b8ff9;
border-right: 1px solid #fff;
padding: 0px;
}
.seller_name {
border-radius: 16px 0 0 0;
}
}
}
// 列头
.columnHeader {
position: relative;
max-height: calc(100% - 55px);
overflow: hidden;
width: 80px;
top: 0px;
.columnHeader-table {
position: relative;
border-spacing: 0px;
tr {
background-color: #fff;
box-sizing: border-box;
td {
font-weight: 600;
border: 1px solid #f7f7f7;
border-top: none;
border-right: none;
}
&:first-child {
td {
padding: 0px;
box-sizing: border-box;
}
}
}
}
}
// 表格本体
.tableBody {
width: 265px;
max-height: calc(100% - 55px);
position: absolute;
top: 52px;
left: 80px;
overflow: auto;
.tableBody-table {
border-spacing: 0px;
tr {
height: 35px;
&:nth-child(odd) {
background: #e8f7ff;
// border: 0.02667rem solid #f7f7f7;
}
&:nth-child(even) {
background: #fff9ed;
}
td {
height: 100%;
font-size: 12px;
text-align: center;
// font-weight: 500;
padding: 0px;
border: 1px solid #f7f7f7;
border-top: none;
border-right: none;
box-sizing: border-box;
&:last-child {
border-right: 1px solid #f7f7f7;
}
}
}
}
}
// 表格元素
tr {
width: fit-content;
white-space: normal;
display: flex;
.td {
display: block;
display: flex;
justify-content: center;
align-items: center;
height: 35px;
color: #262626;
text-align: center;
box-sizing: border-box;
}
.seller_name {
width: 80px;
box-sizing: border-box;
}
.seller_phone,
.one_branch,
.two_branch {
width: 84px;
text-align: center;
box-sizing: border-box;
}
.total_uv,
.total_time,
.add_uv {
width: 64px;
text-align: center;
box-sizing: border-box;
}
.add_time {
width: 71px;
text-align: center;
box-sizing: border-box;
}
}
}
3. jsで表ヘッダーと最初の列のスクロールを実現
scrollFn (event) {
const col = document.getElementById('columnHeader-table')
col.style.top = -event.target.scrollTop + 'px'
const row = document.getElementById('rowHeader-table')
row.style.left = -event.target.scrollLeft + 'px'
},
4. 発生したバグの調整
バグ1: iOSのラバーバンド効果
解決策: iOS スクロールのラバー バンド効果に対処する_Duoduo の小さな赤ちゃんのブログ - CSDN ブログ
バグ2: iOSの縦スクロールと横スクロールが独立していない(横スライド中は縦スクロールがわずかにスクロールし、縦スクロール中は横スクロールがわずかにスクロールする)
解決策: iOS 独自のスクロールを無効にし、コードを使用してスクロール効果を実現します (ただし、スクロールはブラウザのスクロールほどスムーズではありません。より良い解決策がある場合は、一緒に話し合ってください)
async mounted () {
if (/(iPhone|iPad|iPod)/i.test(navigator.userAgent)) {
console.log('是iOS设备')
// 阻止默认的处理方式(阻止下拉滑动的效果)
// passive 参数不能省略,用来兼容ios和android
const tableBody = document.getElementById('tableBody')
// 开始滑动的
let startPos, endPos, isScrolling, scrollTime
tableBody.addEventListener('touchstart', function (e) {
var touch = e.targetTouches[0] // touches数组对象获得屏幕上所有的touch,取第一个touch
// 开始滑动的时间戳
scrollTime = new Date().getTime()
startPos = { x: touch.pageX, y: touch.pageY, time: +new Date() } // 取第一个touch的坐标值
if (tableBody.offsetHeight > tableBody.offsetWidth) {
e.preventDefault()
}
}, { passive: false })
// 触摸移动
tableBody.addEventListener('touchmove', function (e) {
var touch = e.targetTouches[0]
endPos = { x: touch.pageX - startPos.x, y: touch.pageY - startPos.y }
isScrolling = Math.abs(endPos.x) < Math.abs(endPos.y) ? 1 : 0 // isScrolling为1时,表示纵向滑动,0为横向滑动
if (isScrolling == 0) { // 横向
// 横向滚动纵向不变
const time = (new Date().getTime() - scrollTime) / 1000
const speed = Math.abs(endPos.x) / time
// 判断左滑还是右滑,在左端就禁止左滑
if (endPos.x > 0 && tableBody.scrollLeft == 0 && endPos.x < startPos.x) { // 右滑
console.log('左滑')
e.preventDefault()
} else {
console.log('endPos.x ', endPos.x)
if (endPos.x < 0) {
tableBody.scrollTo({ left: `${tableBody.scrollLeft + speed * 0.7}`, behavior: 'smooth' })
} else {
tableBody.scrollTo({ left: `${tableBody.scrollLeft - speed * 0.7}`, behavior: 'smooth' })
}
const row = document.getElementById('rowHeader-table') // 表头
row.style.left = -tableBody.scrollLeft + 'px'
}
}
if (isScrolling == 1) { // 纵向
const time = (new Date().getTime() - scrollTime) / 1000
const speed = Math.abs(endPos.y) / time
console.log('speed', speed)
// console.log('hh_endPos.y', endPos.y) // 上滑<0,下滑》0
if (tableBody.scrollTop == 0 && endPos.y > 0) {
e.preventDefault()
} else {
// 纵向滚动横向不变
if (endPos.y < 0) {
tableBody.scrollTo({ top: `${tableBody.scrollTop + speed * 0.8}`, behavior: 'smooth' })
} else {
tableBody.scrollTo({ top: `${tableBody.scrollTop - speed * 0.8}`, behavior: 'smooth' })
}
const col = document.getElementById('columnHeader-table')// 首列
col.style.top = -tableBody.scrollTop + 'px'
}
}
}, { passive: false })
}
},